FPC Unicode support/es

From Free Pascal wiki
Jump to navigationJump to search

English (en) español (es) français (fr) русский (ru)

Introducción

Hasta FPC 2.6.x, incluido FPC 2.6.x, el RTL se basaba en los de Turbo Pascal y Delphi 7. Esto significa que se basaba principalmente en los tipos shortstring, ansistring y pchar. Ninguno de estos tipos tenía ninguna información de codificación asociada a ellos, pero se supone implícitamente que está codificado en la "codificación predeterminada del sistema" y se transmitió a las llamadas a la API del S.O. sin ninguna conversión.

En Delphi 2009, Embarcadero cambió toda la RTL al tipo UnicodeString, que representa cadenas mediante UTF-16. Además, también hicieron que el tipo AnsiString "code page-aware" (reconocimiento de página de códigos). Esto significa que AnsiStrings a partir de entonces contienen la página de códigos según la cual se deben interpretar sus datos.

La compatibilidad de nivel de lenguaje de FPC para estos tipos de cadena ya está disponible en las versiones estables actuales del compilador (FPC 3.0.0 y versiones superiores). El soporte de nivel RTL aún no está completo. Esta página ofrece una visión general del comportamiento relacionado con la página de códigos de estos tipos de cadena, el nivel actual de compatibilidad en el RTL y las posibles formas futuras de cómo se puede mejorar esta compatibilidad.

Retrocompatibilidad

Si tiene código existente que funciona de forma definida (*) con una versión anterior de FPC y no realiza ningún cambio en él, debe seguir funcionando sin modificar con la nueva versión de FPC. Garantizar esto es el propósito principal de la multitud de variables default*CodePage y sus valores predeterminados como se describe a continuación.

(*) esto significa principalmente: no se almacenan datos en una ansistring que se ha codificado con algo más que la página de códigos predeterminada del sistema y, posteriormente, se pasa esta cadena tal cual a una rutina FPC RTL. Por ejemplo, el código Lazarus actual es generalmente fino, ya que se supone que debe llamar a UTF8ToAnsi() antes de pasar sus cadenas a las rutinas FPC RTL.

Si su código existente usó ansistrings de una manera no soportada, es decir, almacenando datos en él que no están codificados en la página de código predeterminado del sistema y no teniendo cuidado al interactuar con otro código (como las rutinas RTL), todavía puede ser capaz de trabajar en la mayoría de los problemas si estos datos siempre utilizan la misma codificación. En ese caso, puede llamar a SetMultiByteConversionCodePage() al iniciar su programa, y como argumento, la página de códigos de los datos que contienen las ansistrings. Tenga en cuenta que esto también afectará a la interpretación de todos los datos ShortString, AnsiChar y PAnsiChar.

Código de páginas

Un código de páginas define cómo se deben interpretar los bytes individuales de una cadena (string), es decir, qué letra, símbolo u otro carácter gráfico corresponde a cada byte o secuencia de bytes.

Identificadores de códigos de página

Un identificador de código de página siempre se almacena como un TSystemCodePage, que es un alias para Word. El valor representa el código correspondiente según lo definido por Microsoft Windows. Además, hay 3 valores de códigos de página especiales:

  • CP_ACP: este valor representa el código de página del sistema establecido actualmente "código de página predeterminado del sistema". Consulte #Code page settings para obtener más información.
  • CP_OEM: este valor representa el código de página OEM. En plataformas Windows, esto corresponde al código de página utilizado por consola (por ejemplo, ventanas cmd.exe). En otras plataformas este valor se interpreta igual que CP_ACP.
  • CP_NONE: este valor indica que no se ha asociado ninguna información de código de página con los datos de cadena. El resultado de cualquier operación en una cadena que tiene este código de páginas dinámico es indefinido. Lo mismo se mantiene para cualquier otro código de página que no esté en la lista anterior, pero a diferencia de los otros valores de códigos de página no válidos, CP_NONE tiene un significado especial en caso de que se utilice como código de página declarado.

Nota: los identificadores de códigos de página son diferentes de los nombres de códigos de página que se utilizan en las directivas {$codepage xxx} (que ya está disponible en la versión estable de FPC actual). Los nombres de códigos de páginas son los nombres de unidades de códigos de página individuales expuestos por la unidad charset, que tienen nombres como cp866, cp1251 y utf8.

Configuración del código de página

La unidad del sistema contiene varias variables globales que indican el código de página predeterminado utilizado para determinadas operaciones.

DefaultSystemCodePage

  • Propósito: determina cómo se interpreta CP_ACP.
  • Valor inicial:
    • El resultado de la llamada al sistema operativo GetACP, que devuelve el código de página ANSI de Windows.
    • iOS: CP_ACP si no hay ningún widestring manager instalado, de lo contrario UTF-8.
    • Unix (excluyendo iOS): CP_ACP si no hay ningún widestring manager instalado, de lo contrario se basa en las variables de entorno LANG o LC_CTYPE actualmente establecidas. Esto suele ser UTF-8, pero no se garantiza que sea el caso.
    • OS/2: página de códigos actual proporcionada en el primer valor devuelto por DosQueryCP y luego traducido al número de página de códigos utilizado para el mismo juego de caracteres en MS Windows (porque eso es lo que se ha utilizado en Delphi originalmente y la implementación de FPC intenta ser compatible con Delphi); es posible aplicar siempre utilizando números de página de códigos nativos oS/2 en FPC RTL cambiando la variable booleana RTLUsesWinCP a false (el valor predeterminado es true). Tenga en cuenta que los números de página de códigos son en gran medida idénticos para OS/2 y MS Windows con páginas de códigos permitidas para la página de códigos de proceso actual en OS/2 (los números de página de códigos son diferentes para las llamadas páginas de códigos ANSI, páginas de códigos ISO-8859-x y páginas de códigos de Mac OS y páginas de códigos de Mac OS , pero ninguno de estos puede utilizarse como página de códigos de proceso actual en OS/2).
    • Otras plataformas: CP_ACP (estas plataformas actualmente no admiten varias páginas de códigos y están codificadas de forma rígida para usar su página de códigos específica del sistema operativo en todos los casos).
  • Modificaciones: se puede modificar este valor con un llamando a SetMultiByteConversionCodePage (CodePage: TSystemCodePage).
  • Notas: Dado que el valor de esta variable se puede cambiar, no es una buena idea utilizar su valor para determinar el código de página real predeterminado del sistema operativo (a menos que lo haga al inicio del programa y esté seguro de que ninguna otra unidad lo ha cambiado en su código de inicialización).

DefaultFileSystemCodePage

  • Propósito: define la página de códigos a la que se traducen los nombres de archivo/ruta antes de que se pasen a las llamadas a la API del SO, si la RTL utiliza una API de SO de un solo byte para este propósito en la plataforma actual. Esta página de códigos también se utiliza para operaciones intermedias en rutas de acceso de archivo dentro de la RTL antes de realizar llamadas a la API del SO. Esta variable no existe en Delphi y se ha introducido en FPC para que sea posible cambiar el valor de DefaultSystemCodePage sin interrumpir las interfaces RTL con las llamadas a la API del sistema de archivos del sistema operativo.
  • Valor inicial:
    • Windows: UTF-8, porque el RTL utiliza llamadas a la API del sistema operativo UTF-16 (por lo que no se pierden datos en operaciones intermedias).
    • OS X e iOS: DefaultSystemCodePage si no hay ningún widestring manager instalado, de lo contrario UTF-8 (según lo definido por Apple).
    • Unix (excluyendo OS X e iOS): DefaultSystemCodePage, porque la codificación de nombres de archivo es indefinida en plataformas Unix (es una matriz sin tipo de bytes que se puede interpretar de cualquier manera; no se garantiza que sea válida UTF-8).
    • DefaultSystemCodePage, porque OS/2 no proporciona ninguna posibilidad de especificar una página de códigos diferente para las operaciones de E/S de archivo que no sea la página de códigos de todo el proceso actual.
    • Otras plataformas: igual que DefaultSystemCodePage.
  • Modificaciones: puede modificar este valor llamando a SetMultiByteFileSystemCodePage(CodePage: TSystemCodePage); tenga en cuenta que en OS/2 esta variable se está sincronizando con la página de códigos de proceso actual (establecida, por ejemplo, por DosSetProcessCP) durante todas las operaciones de E/S de archivos con el fin de evitar transformaciones no válidas.
  • Notas: la configuración de Unix/OS X/iOS solo se aplica en caso de que se instale el administrador de cadenas anchas cwstring, de lo contrario DefaultFileSystemCodePage tendrá el mismo valor que DefaultSystemCodePage después del inicio del programa.

DefaultRTLFileSystemCodePage

  • Propósito: define la página de códigos a la que se traducen los nombres de archivo/ruta de acceso antes de que se devuelvan desde las rutinas RTL de ruta/archivo RawByteString. Entre los ejemplos se incluyen los nombres de archivo/ruta de acceso devueltos por las versiones RawbyteString de SysUtils.FindFirst y System.GetDir. La razón principal de su existencia es permitir que el RTL proporcione compatibilidad con versiones anteriores de FPC, ya que estas siempre devolvían cadenas codificadas en cualquier API de un solo byte del sistema operativo utilizada (que normalmente era lo que ahora se conoce como DefaultSystemCodePage).
  • Valor inicial
    • Windows: DefaultSystemCodePage, para retrocompatibilidad.
    • OS X and iOS: DefaultSystemCodePage si no hay un widestring manager instalado, caso contrario UTF-8 para retrocompatibilidad (era siempre UTF-8 en el pasado, ya que eso es lo que devuelven las API de archivos del sistema operativo y no convertimos estos datos).
    • Unix (excluding OS X and iOS): DefaultSystemCodePage, por la misma razón que con DefaultFileSystemCodePage. Establecer esto en un valir diferente a DefaultFileSystemCodePage es una mala idea en estas plataformas, debido a que cualquier conversión de página de códigos puede dañar estas cadenas, ya que se desconoce su codificación inicial.
    • OS/2: igual a DefaultSystemCodePage (por compatibilidad con versiones anteriores y también porque es la opción más natural a menos que necesite jugar con diferentes páginas de códigos)
    • Other platforms: igual a DefaultSystemCodePage
  • Modificaciones: se puede modificar este valor llamando aSetMultiByteRTLFileSystemCodePage(CodePage: TSystemCodePage); se puede usar esta posibilidad para leer y/o escribir archivos con un código de página arbitrario.
  • Notas: igual que para DefaultFileSystemCodePage.

Código de página del archivo fuente

El código de página del archivo fuente determina cómo se interpretan las constantes de cadena, y donde el compilador insertará operaciones de conversión del código de página al asignar un tipo de cadena a otro.

Los códigos de página del archivo fuente se determinan de la siguiente manera:

  • si un archivo contiene la directiva {$codepage xxx} (p.e. {$codepage UTF8}), entonces el código de página del archivo fuente es ése, de otra forma
  • si el archivo comienza con un UTF-8 BOM, entonces el código de página del archivo fuente es UTF-8, caso contrario
  • si la directiva {$modeswitch systemcodepage} está activa, entonces el código de página del archivo fuente es el DefaultSystemCodePage de la computadora en que el compilador mismo está actualmente ejecutándose (p.e. ompilar el código fuente en un sistema diferente puede dar lugar a un programa que se comporta de manera diferente; este conmutador está disponible para la compatibilidad con Delphi y está habilitado de forma predeterminada en la opción {$mode delphiunicode}), de otra forma
  • el código de página del archivo fuente se establece en CP_ACP (para la compatibilidad con versiones anteriores de FPC).

Strings

String/character types

Shortstring

El código de página de un shortstring es implícitamente CP_ACP y por lo tanto siempre será igual al valor actual de Defaultsysticacodepage.

PAnsiChar/AnsiChar

Estos tipos son los mismos que los tipos PChar/Char antiguos. En todos los modos del compilador, excepto en el {$mode delphiunicode}, PChar/Char también son alias para PAnsiChar/AnsiChar. Su código de páginas es implícitamente CP_ACP y, por lo tanto, siempre será igual al valor actual de DefaultSystemCodePage.

PWideChar/PUnicodeChar y WideChar/UnicodeChar

Estos tipos permanecen sin cambios. WideChar/UnicodeChar puede contener una sola unidad de código UTF-16, mientras que PWideChar/PUnicodeChar apunta a una sola matriz o a una matriz de unidades de código UTF-16.

En {$mode delphiunicode}, PChar se convierte en un alias para PWideChar/PUnicodeChar y Char se convierte en un alias para WideChar/UnicodeChar.

UnicodeString/WideString

These types behave the same as in previous versions:

  • Widestring is the same as a "COM BSTR" on Windows, and an alias for UnicodeString on all other platforms. Its string data is encoded using UTF-16.
  • UnicodeString is a reference-counted string with a maximum length of high(SizeInt) UTF-16 code units.

Ansistring

AnsiStrings are reference-counted types with a maximum length of high(SizeInt) bytes. Additionally, they now also have code page information associated with them.

The most important thing to understand about the new AnsiString type is that it both has a declared/static/preferred/default code page (called declared code page from now on), and a dynamic code page. The declared code page tells the compiler that when assigning something to that AnsiString, it should first convert the data to that declared code page (except if it is CP_NONE, see RawByteString below). The dynamic code page is a property of the AnsiString which, similar to the length and the reference count, defines the actual code page of the data currently held by that AnsiString.

Declared code page

The declared code page of an AnsiString can only be defined by declaring a new type as follows:

type
  CP866String = type AnsiString(866); // note the extra "type"

The declared code page of a variable declared as plain AnsiString is CP_ACP. In effect, the AnsiString type is now semantically defined in the System unit as

type
  AnsiString = type AnsiString(CP_ACP);

Another predefined AnsiString(X) type in the System unit is UTF8String:

type
  UTF8String = type AnsiString(CP_UTF8);

Once you have defined such a custom AnsiString(X) type, you can use it to declare variables, parameters, fields etc as usual.

Note that CP_UTF16 and CP_UTF16BE are not valid as code pages for AnsiStrings. The result of defining an AnsiString with such a code page is undefined.

Dynamic code page

If a string with a declared code page SOURCE_CP is assigned to a string with declared code page DEST_CP , then

  • if (SOURCE_CP = CP_NONE) or (DEST_CP = CP_NONE), see RawByteString, otherwise
  • if (source file codepage <> CP_ACP), then if (DEST_CP = CP_ACP) and (SOURCE_CP = source file codepage) or vice versa, no conversion will occur (even if at run time DefaultSystemCodePage has a different value from the source file code page). The reason for the "(source file codepage <> CP_ACP)" condition is backward compatibility with previous FPC versions (while they did not support AnsiStrings with arbitrary code pages, they did always reinterpret AnsiStrings according to the current value of the system code page). Otherwise,
  • if (SOURCE_CP <> DEST_CP), the string data will be converted from codepage X1 to codepage X2 before assignment, whereby CP_ACP will be interpreted as the current value of DefaultSystemCodePage. Otherwise,
  • if (SOURCE_CP = DEST_CP), no codepage conversion will be performed.

These rules mean that it is perfectly possible for an AnsiString variable to get a dynamic code page that differs from its declared code page. E.g. in the third case SOURCE_CP could be CP_ACP, while after the assignment it may have a dynamic code page equal to DefaultSystemCodePage.

Note: as mentioned above, whether or not a potential code page conversion happens only depends on the declared code pages of the involved strings. This means that if you assign one AnsiString(X) to another AnsiString(X) and the former's dynamic code was different from X, the string data will not be converted to code page X by the assignment.

RawByteString

The RawByteString type is defined as

type
  RawByteString = type AnsiString(CP_NONE);

As mentioned earlier, the results of operations on strings with the CP_NONE code page are undefined. As it does not make sense to define a type in the RTL whose behaviour is undefined, the behaviour of RawByteString is somewhat different than that of other AnsiString(X) types.

As a first approximation, RawByteString can be thought of as an "untyped AnsiString": assigning an AnsiString(X) to a RawByteString has exactly the same behaviour as assigning that AnsiString(X) to another AnsiString(X) variable with the same value of X: no code page conversion or copying occurs, just the reference count is increased.

Less intuitive is probably that when a RawByteString is assigned to an AnsiString(X), the same happens: no code page conversion or copying, just the reference count is increased. Note that this means that results from functions returning a RawByteString will never be converted to the destination's declared code page. This is another way in which the dynamic code page of an AnsiString(X) can become different from its declared code page.

This type is mainly used to declare const, constref and value parameters that accept any AnsiString(X) value without converting it to a predefined declared code page. Note that if you do this, the routine accepting those parameters should be able to handle strings with any possible dynamic code page.

var and out parameters can also be declared as RawByteString, but in this case the compiler will give an error if an AnsiString(X) whose declared code page is different from CP_NONE is passed in. This is consistent with var and out parameters in general: they require an exactly matching type to be passed in. You can add an explicit RawByteString() typecast around an argument to remove this error, but then you must be prepared to deal with the fact that the returned string can have any dynamic code page.

String concatenations

Normally, in Pascal the result type of an expression is independent of how its result is used afterwards. E.g. multiplying two longints on a 32 bit platform and assigning the result to an int64 will still perform the multiplication using 32 bit arithmetic, and only afterwards the result is converted to 64 bit.

Code page-aware strings are the only exception to this rule: concatenating two or more strings always occurs without data loss, although afterwards the resulting string will of course still be converted to the declared code page of the destination (which may result in data loss).

Assigning the result of a concatenation to a RawByteString is again special:

  • if all concatenated strings have the same dynamic code page, the result will have this code page too
  • in other cases the result will be converted to CP_ACP (we may add an option in the future to change this RawByteString behaviour, as it is not very practical).

String constants

The compiler has to know the code page according to which it should interpret string constants, as it may have to convert them at compile time. Normally, a string constant is interpreted according to the source file codepage. If the source file codepage is CP_ACP, a default is used instead: in that case, during conversions the constant strings are assumed to have code page 28591 (ISO 8859-1 Latin 1; Western European).

When a string constant is assigned to an AnsiString(X) either in code or as part of a typed constant or variable initialisation, then

  • if X = CP_NONE (i.e., the target is a RawByteString), the result is the same as if the constant string were assigned to an AnsiString(CP_ACP)
  • if X = CP_ACP and the code page of the string constant is different from CP_ACP, then the string constant is converted, at compile time, to the source file code page. If the source file code page is also CP_ACP, it will be stored in the program unaltered with a code page of CP_ACP and hence its meaning/interpretation will depend on the actual value of DefaultSystemCodePage at run time. This ensures compatibility with older versions of FPC when assigning string constants to AnsiString variables without using a {$codepage xxx} directive or UTF-8 BOM.
  • for other values of X, the string constant is converted, at compile time, to code page X

Similarly, if a string constant is assigned to a UnicodeString, the string constant is converted, at compile time, from the source file code page to UTF-16.

For ShortString and PChar, the same rule as for AnsiString(CP_ACP) is followed.

Note that symbolic string constants will be converted at compile time to the appropriate string type and code page whenever they are used. This means that there is no speed overhead when using a single string constant in multiple code page and string type contexts, only some data size overhead.

From the above it follows that to ensure predictable interpretation of string constants in your source code, it is best to either include an explicit {$codepage xxx} directive (or use the equivalent -Fc command line option), or to save the source code in UTF-8 with a BOM.

String indexing

Nothing changes to string indexing. Every string element of a UnicodeString/WideString is two bytes and every string element of all other strings is one byte. The string indexing mechanism completely ignores code pages and composite code points.

RTL changes

In order to fully guarantee data integrity in the presence of codepage-aware strings, all routines in the RTL and packages that accept AnsiString parameters must be adapted. The reason is that if their parameters remain plain AnsiString, then any string with a different declared code page will be converted to DefaultSystemCodePage when it is passed in. This can result in data loss.

Until now, primarily routines dealing with file system access have been updated to preserve all character data. Below is an exhaustive list of all routines that preserve the string encoding in FPC 3.0. Unless where explicitly noted otherwise, these routines also all have overloads that accept UnicodeString parameters.

  • System: FExpand, LowerCase, UpperCase, GetDir, MKDir, ChDir, RMDir, Assign, Erase, Rename, standard I/O (Read/Write/Readln/Writeln/Readstr/Writestr), Insert, Copy, Delete, SetString
  • ObjPas (used automatically in Delphi and ObjFPC modes): AssignFile
  • SysUtils: FileCreate, FileOpen, FileExists, DirectoryExists, FileSetDate, FileGetAttr, FileSetAttr, DeleteFile, RenameFile, FileSearch, ExeSearch, FindFirst, FindNext, FindClose, FileIsReadOnly, GetCurrentDir, SetCurrentDir, ChangeFileExt, ExtractFilePath, ExtractFileDrive, ExtractFileName, ExtractFileExt, ExtractFileDir, ExtractShortPathName, ExpandFileName, ExpandFileNameCase, ExpandUNCFileName, ExtractRelativepath, IncludeTrailingPathDelimiter, IncludeTrailingBackslash, ExcludeTrailingBackslash, ExcludeTrailingPathDelimiter, IncludeLeadingPathDelimiter, ExcludeLeadingPathDelimiter, IsPathDelimiter, DoDirSeparators, SetDirSeparators, GetDirs, ConcatPaths, GetEnvironmentVariable
  • Unix: fp*() routines related to file system operations (no UnicodeString overloads), POpen
  • DynLibs: all routines

RTL todos

As the above list is exhaustive, no other RTL routines support arbitrary code pages yet. This section contains a list of gotchas that some people have identified and, if possible, workarounds. Note that routines not mentioned here nor above are equally unsafe as the ones that are explicitly mentioned.

TFormatSettings and DefaultFormatSettings

The type of ThousandSeparator and DecimalSeparator is AnsiChar type. This means that if DefaultSystemCodePage is UTF-8 and the locale's separator is more than one byte long in that encoding, these fields are not large enough. Examples are the French and Russian non-breaking white space character used to represent the ThousandSeparator.

Old/obsolete sections

Warning-icon.png

Warning: These sections are kept for historical reference - please update the sections above with this information if it is still applicable. Since FPC 2.7 (development version before the release of 3.0.0), extensive Unicode support has been implemented.


User visible changes

Full support of code page aware strings is not possible without breaking some existing code. The following list tries to summarize the most important user visible changes.

  • The string header has two new fields: encoding and element size. On 32 Bit platforms this increases the header size by 4 and on 64 bit platforms by 8 bytes.
  • WideCharLenToString, UnicodeCharLenToString, WideCharToString, UnicodeCharToString and OleStrToString return an UnicodeString instead of an Ansistring before.
  • the type of the dest parameter of WideCharLenToString and UnicodeCharLenToString has been changed from Ansistring to Unicodestring
  • UTF8ToAnsi and AnsiToUTF8 take a RawByteString now

See Also