Seleccionar una codificación para el modo texto

En el caso de los archivos de texto escrito, la codificación debe elegirse en función de las características del texto o ajustarse a los requisitos de los programas externos a los que van destinados los archivos generados. Si no existen requisitos externos, puede seguir la regla de utilizar siempre ANSI para textos sin formato con números, letras inglesas y signos de puntuación (se ofrece una tabla de esos 128 caracteres internacionales en la sección Comparación de cadenas). Cuando trabaje con varios idiomas o caracteres especiales, utilice UTF-8 o Unicode, respectivamente:

int u8 = FileOpen("utf8.txt"FILE_WRITE | FILE_TXT | FILE_ANSI0CP_UTF8);
int u0 = FileOpen("unicode.txt"FILE_WRITE | FILE_TXT | FILE_UNICODE);

Por ejemplo, estos ajustes son útiles para guardar los nombres de los instrumentos financieros en un archivo, ya que a veces utilizan caracteres especiales que denotan divisas o modos de trading.

La lectura de sus propios archivos no debería ser un problema, ya que basta con especificar al leer la misma configuración de codificación que al escribir. No obstante, los archivos de texto pueden proceder de distintas fuentes. Su codificación puede ser desconocida o estar sujeta a cambios sin previo aviso. Por tanto, se plantea la cuestión de qué hacer si algunos de los archivos pueden suministrarse como cadenas de un solo byte (ANSI), otros como cadenas de dos bytes (Unicode) y otros como codificación UTF-8.

La codificación puede seleccionarse mediante los parámetros de entrada del programa. Sin embargo, esto sólo es efectivo para un archivo, y si tiene que abrir muchos archivos diferentes, es posible que sus codificaciones no coincidan. Por lo tanto, es conveniente dar instrucciones al sistema para que elija el modelo correcto sobre la marcha (de archivo a archivo).

MQL5 no permite la detección 100 % automática y la aplicación de codificaciones correctas; sin embargo, hay un modo más universal para leer una variedad de archivos de texto. Para ello, necesita configurar los siguientes parámetros de entrada de la función FileOpen:

int h = FileOpen(filenameFILE_READ | FILE_TXT | FILE_ANSI0CP_UTF8);

Hay varios factores en juego.

En primer lugar, la codificación UTF-8 omite de forma transparente los mencionados 128 caracteres en cualquier codificación ANSI (es decir, se transmiten «uno a uno»).

En segundo lugar, es el más popular para protocolos de Internet.

En tercer lugar, MQL5 tiene un análisis adicional integrado para el formato de texto en Unicode de dos bytes, que le permite cambiar automáticamente el modo de operación de archivo a FILE_UNICODE, si es necesario, con independencia de los parámetros especificados. Y es que los archivos en formato Unicode suelen ir precedidos de un par especial de identificadores: 0xFFFE, o viceversa, 0xFEFF. Esta secuencia se denomina Byte Order Mark (BOM). Se necesita porque, como sabemos, los bytes pueden almacenarse dentro de números en un orden diferente en distintas plataformas (esto se abordó en la sección Control de la codificación endian de números enteros).

El formato FILE_UNICODE utiliza un entero de 2 bytes (código) por carácter, por lo que el orden de los bytes adquiere importancia, a diferencia de otras codificaciones. El orden de bytes BOM de Windows es 0xFFFE. Si el núcleo MQL5 encuentra esta etiqueta al principio de un archivo de texto, su lectura cambiará automáticamente al modo Unicode.

Veamos cómo funcionan las distintas configuraciones de modo con archivos de texto de distintas codificaciones. Para ello, utilizaremos el script FileText.mq5 y varios archivos de texto con el mismo contenido, pero en diferentes codificaciones (entre paréntesis se indica el tamaño en bytes):

  • ansi1252.txt (50): codificación europea 1252 (se mostrará completa sin distorsión en Windows con el idioma europeo)
  • unicode1.txt (102): Unicode de dos bytes, al principio está la lista de materiales (BOM) inherente a Windows 0xFFFE
  • unicode2.txt (100): Unicode de dos bytes sin lista de materiales BOM (en general, la lista de materiales es opcional)
  • unicode3.txt (102): Unicode de dos bytes, al principio hay BOM inherente a Unix, 0xFEFF
  • utf8.txt (54): codificación UTF-8

En la función OnStart leeremos estos archivos en bucles con diferentes ajustes de FileOpen. Tenga en cuenta que al utilizar FileHandle (revisado en la sección anterior) no tenemos que preocuparnos de cerrar archivos: todo ocurre automáticamente dentro de cada iteración.

void OnStart()
{
   Print("=====> UTF-8");
   for(int i = 0i < ArraySize(texts); ++i)
   {
      FileHandle fh(FileOpen(texts[i], FILE_READ | FILE_TXT | FILE_ANSI0CP_UTF8));
      Print(texts[i], " -> "FileReadString(~fh));
   }
   
   Print("=====> Unicode");
   for(int i = 0i < ArraySize(texts); ++i)
   {
      FileHandle fh(FileOpen(texts[i], FILE_READ | FILE_TXT | FILE_UNICODE));
      Print(texts[i], " -> "FileReadString(~fh));
   }
   
   Print("=====> ANSI/1252");
   for(int i = 0i < ArraySize(texts); ++i)
   {
      FileHandle fh(FileOpen(texts[i], FILE_READ | FILE_TXT | FILE_ANSI01252));
      Print(texts[i], " -> "FileReadString(~fh));
   }
}

La función FileReadString lee una cadena de un archivo. Lo abordaremos en la sección sobre escritura y lectura de variables.

A continuación se muestra un registro de ejemplo con los resultados de la ejecución del script:

=====> UTF-8
MQL5Book/ansi1252.txt -> This is a text with special characters: ?? / ? / ?
MQL5Book/unicode1.txt -> This is a text with special characters: ±Σ / £ / ¥
MQL5Book/unicode2.txt -> T
MQL5Book/unicode3.txt -> ??
MQL5Book/utf8.txt -> This is a text with special characters: ±Σ / £ / ¥
=====> Unicode
MQL5Book/ansi1252.txt -> 桔獩椠⁳⁡整瑸眠瑩⁨灳捥慩档牡捡整獲›㾱⼠ꌠ⼠ꔠ
MQL5Book/unicode1.txt -> This is a text with special characters: ±Σ / £ / ¥
MQL5Book/unicode2.txt -> This is a text with special characters: ±Σ / £ / ¥
MQL5Book/unicode3.txt -> 吀栀椀猀 椀猀 愀 琀攀砀琀 眀椀琀栀 猀瀀攀挀椀愀氀 挀栀愀爀愀挀琀攀爀猀㨀 넀
MQL5Book/utf8.txt -> 桔獩椠⁳⁡整瑸眠瑩⁨灳捥慩档牡捡整獲›뇂ꏎ⼠술₣ ꗂ
=====> ANSI/1252
MQL5Book/ansi1252.txt -> This is a text with special characters: ±? / £ / ¥
MQL5Book/unicode1.txt -> This is a text with special characters: ±Σ / £ / ¥
MQL5Book/unicode2.txt -> T
MQL5Book/unicode3.txt -> þÿ
MQL5Book/utf8.txt -> This is a text with special characters: Â±Î£ / Â£ / Â¥

El archivo unicode1.txt siempre se lee correctamente porque tiene BOM 0xFFFE, y el sistema ignora la configuración del código fuente. No obstante, si falta la etiqueta o es big-endian, esta autodetección no funciona. Además, al establecer FILE_UNICODE, perdemos la capacidad de leer textos de un solo byte y UTF-8.

Como resultado, la combinación mencionada de FILE_ANSI y CP_UTF8 debería considerarse más resistente a las variaciones en el formato. Sólo se recomienda seleccionar una página de código nacional específica cuando se requiera explícitamente.

A pesar de la importante ayuda que proporciona la API al programador cuando trabaja con archivos en modo texto, podemos, si es necesario, evitar el modo FILE_TXT o FILE_CSV y abrir un archivo de texto en modo binario FILE_BINARY. Esto hará que toda la complejidad de analizar el texto y determinar la codificación recaiga sobre los hombros del programador, pero le permitirá admitir otros formatos no estándar. No obstante, lo más importante es que se puede leer y escribir texto en un archivo abierto en modo binario. Sin embargo, lo contrario, en el caso general, es imposible. Un archivo binario con datos arbitrarios (es decir, que no contenga exclusivamente cadenas) abierto en modo texto se interpretará muy probablemente como un «galimatías» de texto. Si necesita escribir datos binarios en un archivo de texto, utilice primero la función CryptEncode y la codificación CRYPT_BASE64.