Trabajar con símbolos y páginas de códigos
Dado que las cadenas están formadas por caracteres, a veces es necesario o simplemente más conveniente manipular caracteres individuales o grupos de caracteres de una cadena a nivel de los códigos de sus enteros. Por ejemplo: usted necesita leer o sustituir caracteres de uno en uno o convertirlos en arrays de códigos de enteros para su transmisión a través de protocolos de comunicación o en interfaces de programación de terceros de bibliotecas dinámicas DLL. En todos estos casos, pasar cadenas como texto puede ir acompañado de diversas dificultades:
- garantizar la codificación correcta (de las que hay muchas, y su elección depende de la configuración regional del sistema operativo, los ajustes del programa, la configuración de los servidores con los que se comunica, etc.);
- la conversión de los caracteres de lenguas nacionales desde la codificación de texto local a Unicode y viceversa;
- la asignación y anulación de la asignación de memoria de forma unificada.
El uso de arrays con códigos de enteros (aunque tal uso produce en realidad una representación binaria y no textual de la cadena) simplifica estos problemas.
La API de MQL5 proporciona un conjunto de funciones para operar con caracteres individuales o sus grupos, teniendo en cuenta las características de codificación.
Las cadenas en MQL5 contienen caracteres en codificación Unicode de dos bytes, lo que proporciona una compatibilidad universal para toda la variedad de alfabetos nacionales en una única (pero muy grande) tabla de caracteres. Dos bytes permiten codificar 65535 elementos.
El tipo de carácter por defecto es ushort. No obstante, si es necesario, la cadena puede convertirse en una secuencia de caracteres uchar de un solo byte en una codificación lingüística específica. Esta conversión puede ir acompañada de la pérdida de cierta información (en particular, las letras que no figuran en la tabla de caracteres localizados pueden «perder» diéresis o incluso «convertirse» en algún tipo de carácter sustitutivo: según el contexto, puede mostrarse de forma diferente, pero normalmente aparece como ' ?' o un carácter cuadrado).
Para evitar problemas con textos que puedan contener caracteres arbitrarios, se recomienda utilizar siempre Unicode. Se puede hacer una excepción si algunos servicios o programas externos que deben integrarse con su programa MQL no admiten Unicode, o si el texto ha sido concebido desde el principio para almacenar un conjunto limitado de caracteres (por ejemplo, sólo números y letras latinas).
Al convertir a/desde caracteres de un solo byte, la API de MQL5 utiliza la codificación ANSI por defecto, dependiendo de la configuración actual de Windows. No obstante, el desarrollador puede especificar una tabla de códigos diferente (véanse otras funciones CharArrayToString, StringToCharArray).
En el archivo StringSymbols.mq5 se ofrecen ejemplos de utilización de las funciones descritas más abajo.
bool StringSetCharacter(string &variable, int position, ushort character)
La función cambia el carácter en position al valor character en la cadena variable pasada. El número debe estar comprendido entre 0 y la longitud de la cadena (StringLen) menos 1.
Si el carácter que se va a escribir es 0, especifica un nuevo final de línea (actúa como un cero terminal), es decir, la longitud de la línea pasa a ser igual a position. El tamaño del búfer asignado a la línea no cambia.
Si el parámetro position es igual a la longitud de la cadena y el carácter que se está escribiendo no es igual a 0, entonces el carácter se añade a la cadena y su longitud se incrementa en 1. Esto equivale a la expresión variable += ShortToString(character).
La función devuelve true si se completa con éxito, o false en caso de error.
void OnStart()
|
ushort StringGetCharacter(string value, int position)
La función devuelve el código del carácter situado en la posición especificada de la cadena. El número de posición debe estar comprendido entre 0 y la longitud de la cadena (StringLen) menos 1. En caso de error, la función devolverá 0.
La función equivale a escribir utilizando el operador '[]': value[position].
string numbers = "0123456789";
|
string CharToString(uchar code)
La función convierte el código ANSI de un carácter en una cadena de un solo carácter. Dependiendo de la página de códigos de Windows configurada, la mitad superior de los códigos (superior a 127) puede generar letras diferentes (el estilo de los caracteres es diferente, mientras que el código sigue siendo el mismo). Por ejemplo, el símbolo con el código 0xB8 (184 en decimal) indica una cedilla (gancho inferior) en los idiomas de Europa occidental, mientras que en la lengua rusa aquí se encuentra la letra «ё». He aquí otro ejemplo:
PRT(CharToString(0xA9)); // "©"
|
string ShortToString(ushort code)
La función convierte el código Unicode de un carácter en una cadena de un solo carácter. Para el parámetro code puede utilizar un literal o un entero. Por ejemplo, la letra griega mayúscula «sigma» (el signo de la suma en las fórmulas matemáticas) puede especificarse como 0x3A3 o 'Σ'.
PRT(ShortToString(0x3A3)); // "Σ"
|
int StringToShortArray(const string text, ushort &array[], int start = 0, int count = -1)
La función convierte una cadena en una secuencia de códigos de caracteres ushort que se copian en la ubicación especificada del array: empezando por el elemento numerado start (0 por defecto, es decir, el principio del array) y en la cantidad de count.
Nota: el parámetro start se refiere a la posición en el array, no en la cadena. Si desea convertir parte de una cadena, primero debe extraerla utilizando la función StringSubstr.
Si el parámetro count es igual a -1 (o WHOLE_ARRAY), se copian todos los caracteres hasta el final de la cadena (incluido el nulo terminal) o los caracteres de acuerdo con el tamaño del array, si es de tamaño fijo.
En el caso de un array dinámico, su tamaño se incrementará automáticamente si es necesario. Si el tamaño de un array dinámico es mayor que la longitud de la cadena, no se reduce el tamaño del array.
Para copiar caracteres sin un nulo de terminación debe llamar explícitamente a StringLen como argumento count. En caso contrario, la longitud del array será 1 más que la longitud de la cadena (y 0 en el último elemento).
La función devuelve el número de caracteres copiados.
...
|
Tenga en cuenta que, si la posición para copiar supera el tamaño del array, los elementos intermedios se asignarán pero no se inicializarán. En consecuencia, pueden contener datos aleatorios (resaltados en amarillo más arriba).
string ShortArrayToString(const ushort &array[], int start = 0, int count = -1)
La función convierte parte del array con códigos de caracteres en una cadena. El rango de los elementos del array se establece mediante los parámetros start y count, es decir, la posición inicial y la cantidad, respectivamente. El parámetro start debe estar comprendido entre 0 y el número de elementos del array menos 1. Si count es igual a -1 (o WHOLE_ARRAY) se copian todos los elementos hasta el final del array o hasta el primer elemento nulo.
Utilizando el mismo ejemplo de StringSymbols.mq5, vamos a intentar convertir un array en la cadena array2, que tiene un tamaño de 30.
...
|
Dado que en el array array2 la cadena «ABCDEABCD» se copió dos veces, y concretamente, la primera vez al principio y la segunda vez en el desplazamiento 20, los caracteres intermedios serán aleatorios y podrán formar una cadena más larga que la nuestra.
int StringToCharArray(const string text, uchar &array[], int start = 0, int count = -1, uint codepage = CP_ACP)
La función convierte la cadena text en una secuencia de caracteres de un solo byte que se copian en la ubicación especificada del array: empezando por el elemento numerado start (0 por defecto, es decir, el principio del array) y en la cantidad de count. El proceso de copia convierte los caracteres de Unicode a la página de códigos seleccionada codepage (por defecto, CP_ACP, que significa el idioma del sistema operativo Windows (más adelante encontrará más información al respecto)).
Si el parámetro count es igual a -1 (o WHOLE_ARRAY), se copian todos los caracteres hasta el final de la cadena (incluido el nulo terminal) o de acuerdo con el tamaño del array, si es de tamaño fijo.
En el caso de un array dinámico, su tamaño se incrementará automáticamente si es necesario. Si el tamaño de un array dinámico es mayor que la longitud de la cadena, no se reduce el tamaño del array.
Para copiar caracteres sin un nulo de terminación debe llamar explícitamente a StringLen como argumento count.
La función devuelve el número de caracteres copiados.
Consulte la lista de páginas de códigos válidas para el parámetro codepage en la documentación. Estas son algunas de las páginas de códigos ANSI más utilizadas:
Idioma |
Código |
---|---|
Alfabeto latino de Europa central |
1250 |
Cirílico |
1251 |
Alfabeto latino de Europa occidental |
1252 |
Griego |
1253 |
Turco |
1254 |
Hebreo |
1255 |
Árabe |
1256 |
Báltico |
1257 |
Así, en ordenadores con idiomas de Europa occidental, CP_ACP es 1252, y, por ejemplo, en ordenadores con ruso, es 1251.
Durante el proceso de conversión, algunos caracteres pueden convertirse con pérdida de información, ya que la tabla Unicode es mucho mayor que ANSI (cada tabla de códigos ANSI tiene 256 caracteres).
En este sentido, CP_UTF8 reviste especial importancia entre todas las constantes CP_***. Permite conservar adecuadamente los caracteres nacionales mediante la codificación de longitud variable: el array resultante sigue almacenando bytes, pero cada carácter nacional puede abarcar varios bytes, escritos en un formato especial. Por ello, la longitud del array puede ser significativamente mayor que la longitud de la cadena. La codificación UTF-8 se utiliza ampliamente en Internet y en diversos programas informáticos. Por cierto: UTF significa «Unicode Transformation Format» (formato de transformación Unicode), y existen otras modificaciones, en particular UTF-16 y UTF-32.
Veremos un ejemplo para StringToCharArray una vez que nos familiaricemos con la función «inversa» CharArrayToString: su trabajo debe demostrarse conjuntamente.
string CharArrayToString(const uchar &array[], int start = 0, int count = -1, uint codepage = CP_ACP)
La función convierte un array de bytes o parte de él en una cadena. El array debe contener caracteres en una codificación específica. El rango de los elementos del array se establece mediante los parámetros start y count, es decir, la posición inicial y la cantidad, respectivamente. El parámetro start debe estar comprendido entre 0 y el número de elementos del array. Cuando count es igual a -1 (o WHOLE_ARRAY) se copian todos los elementos hasta el final del array o hasta el primer elemento nulo.
Veamos cómo funcionan las funciones StringToCharArray y CharArrayToString con diferentes caracteres nacionales con diferentes configuraciones de página de códigos. Para ello se ha preparado un script de prueba StringCodepages.mq5.
Se utilizarán dos líneas como sujetos de prueba, en ruso y en alemán:
void OnStart()
|
Las copiaremos en los arrays bytes1 y bytes2, y luego las recuperaremos como cadenas.
En primer lugar, convirtamos el texto alemán utilizando la página de códigos europeos 1252.
...
|
En las copias europeas de Windows, esto equivale a la llamada a una función más sencilla con parámetros por defecto, porque allí CP_ACP = 1252:
StringToCharArray(german, bytes1); |
A continuación, restauramos el texto del array con la siguiente llamada y nos aseguramos de que todo coincide con el original:
...
|
Ahora vamos a intentar convertir el texto ruso en la misma codificación europea (o puede llamar a StringToCharArray(english, bytes2) en el entorno de Windows donde CP_ACP está configurado en 1252 como página de códigos por defecto):
...
|
Aquí ya puede ver que hubo un problema durante la conversión porque 1252 no tiene cirílico. Restaurar una cadena a partir de un array muestra claramente la esencia:
...
|
Repitamos el experimento en un entorno ruso condicional, es decir, convertiremos ambas cadenas de ida y vuelta utilizando la página de códigos cirílicos 1251.
...
|
Se hace así evidente la fragilidad de las codificaciones de un solo byte.
Por último, activemos la codificación CP_UTF8 para ambas cadenas de prueba. Esta parte del ejemplo funcionará de forma estable independientemente de la configuración de Windows.
...
|
Observe que ambas cadenas codificadas en UTF-8 requieren arrays más grandes que las ANSI. Además, el array con el texto ruso se ha hecho 2 veces más largo, porque ahora todas las letras ocupan 2 bytes. Quienes lo deseen pueden encontrar detalles en fuentes abiertas sobre cómo funciona exactamente la codificación UTF-8. En el contexto de este libro, es importante para nosotros que la API de MQL5 proporcione funciones ya hechas con las que trabajar.