Descargar MetaTrader 5

Fundamentos de programación en MQL5 - Cadenas de caracteres

21 abril 2014, 16:10
Dmitry Fedoseev
0
1 145

Introducción

Las cadenas, o para ser más exacto las variables de cadenas (variables string), sirven para almacenar los datos de caracteres, es decir, el texto:

string str="Cualquier texto";

El lenguaje MQL5 ofrece un amplio abanico de cómodas prestaciones para trabajar con las cadenas de caracteres. En programación de los Asesores Expertos (EA) e indicadores, con mayor frecuencia las cadenas se utilizan para crear los mensajes informativos. En los indicadores puede tratarse de los mensaje sobre la ejecución de algunas condiciones (por ejemplo, señales de trading), en los EAs pueden ser los mensajes sobre los resultados de ejecución de acciones de trading. Cuando se inicia un EA, script o indicador, puede ejecutarse el chequeo de los parámetros establecidos por el usuario notificándole sobre los parámetros inválidos. Aparte de las notificaciones, a veces puede salir un mensaje de ayuda con las sugerencias respecto a la configuración de parámetros. En fin, durante la programación en MQL5, el uso de las cadenas ofrece en primer lugar la comodidad para el usuario.

Además de la comodidad, el trabajo con las cadenas es imprescindible cuando trabajamos con los archivos. La escritura y la lectura desde los archivos se realiza utilizando las variables de cadenas. Desde luego, existe otro modo de trabajar con los archivos: se trata del método binario que asegura la lectura y la escritura de variables numéricas y arrays. Sin embargo, si los volúmenes de datos son pequeños, es mejor utilizar los archivos de texto y las cadenas. En este caso el funcionamiento del programa es más transparente para el usuario, el proceso de desarrollo del programa también resulta ser más fácil ya que se asegura el control directo de datos. En los archivos de texto los datos tienen el mismo aspecto que los datos dentro del programa.

El uso de las cadenas puede ampliar considerablemente las posibilidades del programa en cuanto a la entrada de datos (parámetros) en los casos cuando no se sabe de antemano el número necesario de parámetros (por ejemplo, el tamaño de lotes para aumentos medios). En este caso se puede escribir los valores en una línea separados por algún separador, por ejemplo, punto y coma:

input string Lots="0.1; 0.2; 0.3; 0.5";

Luego, durante la inicialización del EA, la cadena se divide y se llena el array de valores numéricos. Desafortunadamente, no se puede repasar los parámetros string durante la optimización, es decir, establecer el valor inicial y final y el paso. En algunos casos, tal vez sea más preferible utilizar una gran cantidad de variables numéricas en la ventana de propiedades. Pero su cantidad es prácticamente ilimitada, y entonces surge la pregunta en cuanto a la comodidad y el objetivo (si es necesaria la posibilidad de optimización).

También pueden tener lugar otros casos cuando es seguro que la optimización del parámetro no va a ser necesaria (por ejemplo, activación de notificaciones). El lenguaje MQL5 soporta una variedad de notificaciones del usuario: sonido, ventana, envío del e-mail, notificaciones push. Usted puede crear para cada una de estas notificaciones su propio interruptor de tipo bool en la ventana de propiedades (va a necesitar como mínimo cuatro variables), o bien reducir el número de variables a una variable string.

Si hay que activar el sonido, se pone "s" (sound), si hay que activar e-mail también, pues se añade "e". De esta manera, se puede activar cualquier combinación de notificaciones utilizando sólo una variable. Para un EA, el número de parámetros externos tiene poca importancia. Es sólo la cuestión de comodidad para el usuario.

En cuanto al desarrollo de los indicadores, hay que tratar de reducir el número de parámetros externos. Es muy probable que el indicador vaya a invocarse desde el EA u otro indicador utilizando las funciones iCustom() o IndicatorCreate() cuyo número de parámetros está limitado (iCustom() tiene sólo 64 parámetros, el tamaño del array de parámetros de la función IndicatorCreate() tiene 64 elementos). Por eso el uso de cadenas puede ser de gran valor práctico.

En este artículo analizaremos todas las funciones estándar del lenguaje que se utilizan para trabajar con las cadenas, y crearemos también algunas funciones útiles personalizadas.

 

Declaración de la variable de cadenas

Como cualquier otro tipo de variables, las variables de cadenas no sólo pueden ser declaradas:

string str;
Sino también pueden declararse con la asignación inmediata de un valor (inicializarse con un valor):
string str="Cualquier texto";

No existe ninguna limitación en cuanto a la longitud de la cadena. Por el tema de comodidad, las cadenas largas pueden ir en varias subcadenas:

string str= "Una cadena larga "
            "puede escribirse en "
            "varias subcadenas";
//--- mostrar resultado
Alert(str);

Con esta forma de inicialización, la variable str va a contener la cadena "Una cadena larga puede escribirse en varias subcadenas".

Aquí, adelantándose un poco, cabe mencionar que el valor de una variable string declarada sin parámetros no es igual a una cadena vacía:

string str="";

Puede verlo por sí mismo:

string str1;
string str2="";
//--- mostrar resultados de comparación
Alert(str1==str2);

Al ejecutar este código, se muestra la ventana de diálogo "false". La variable string no inicializada tiene el valor NULL, que no es el mismo que una cadena vacía "". ¡Hay que tenerlo presente! A la hora de trabajar con las cadenas, muy a menudo tenemos que comprobar si una cadena es vacía o no. Por eso tenemos que seguir la regla de inicializar todas las cadenas con una cadena vacía "" o realizar dos comprobaciones: comprobando la no igualdad a "" y la no igualdad a NULL:

if(str!="" && str!=NULL)
  {
   //--- alguna operación con la cadena
  }

El primer método es más preferible ya que simplifica la condición de la verificación. 

Se puede hacer la misma verificación comprobando el tamaño de la variable. Para determinar el tamaño, se utiliza la función StringLen():

if(StringLen(str)!=0)
  { 
   //--- alguna operación con la cadena
  }


Concatenación de cadenas

La principal operación que Usted hará con más frecuencia a la hora de trabajar con las cadenas será su concatenación, es decir, la construcción de frases utilizando las palabras. La concatenación se realiza utilizando el signo "+":

string str1,str2,str3,str4,str5;
//--- asignar valores
str1="La programación";
str2="en MQL5";
str3="para MetaTrader 5";
//--- concatenación de cadenas
str4=str1+" "+str2;
str5=str1+" "+str3;
//--- mostrar resultados
Alert(str4);
Alert(str5);

Después de la ejecución de este código, la variable str4 va a contener la frase "Programación en MQL5", y la variable str5 tendrá la frase "Programación para MetaTrader 5". En este ejemplo hemos concatenado dos cadenas, y hemos asignado la cadena obtenida a otra variable.

Muy a menudo se realiza la concatenación de una cadena adicional a la cadena principal: 

string str1,str2,str3;
//--- asignar valores
str1="La programación";
str2="en MQL5";
str3="para MetaTrader 5";
//--- concatenación de cadenas con la cadena principal
str1=str1+" "+str2;
str1=str1+" "+str3;
//--- mostrar resultado
Alert(str1);

Después de la ejecución de este código la cadena str1 va a contener la frase "Programación en MQL5 para MetaTrader 5". En este ejemplo se ha realizado la concatenación de cadenas con la cadena principal str1, con la asignación del resultado obtenido a la misma. Estas mismas operaciones podemos escribir de forma un poco más sencilla:

str1+=str2;
str1+=str3;
O así:
str1+=str2+str3;

El signo "+" a la izquierda del signo "=" significa que a la variable str1 se le añade la expresión que figura a la derecha del signo "=".

Además, se puede insertar una cadena al principio de la cadena principal. Eso se hace de forma parecida que se muestra en el penúltimo ejemplo: la cadena principal se añade a la cadena adicional, y la cadena obtenida se asigna a la variable principal:

string str1,str2,str3;
//--- asignar valores
str1="La programación";
str2="en MQL5";
str3="para MetaTrader 5";
//--- concatenación de cadenas con adición de la cadena al principio
str3=str2+" "+str3;
str3=str1+" "+str3;
//--- mostrar resultado
Alert(str3);

Pero ahora la frase "Programación en MQL5 para MetaTrader 5" va a estar en la variable str3. Lo mismo podemos hacer en una línea: 

str3=str1+" "+str2+" "+str3;
En algunos casos se puede concatenar utilizando "," (coma). Eso es posible con las funciones Alert(), Print(), Comment():
Print(str1," ",str2," ",str3);

Los resultados finales en este caso serán los mismos que el uso del signo "+":

Print(str1+" "+str2+" "+str3);

En realidad, el signo "," no realiza la concatenación de las cadenas. En todas las funciones la coma es un separador de parámetros. Lo mismo pasa con las funciones Alert(), Print(), Comment(). Estas funciones tienen un parámetro obligatorio y una gran cantidad de parámetros opcionales. En realidad, se realiza la transferencia de unos parámetros a la función donde ellos se concatenan. El número máximo de parámetros es 64.

Podemos notar algo parecido al escribir una cadena en el archivo mediante la función FileWrite(). Sin embargo, si abrimos el archivo en el modo FILE_CSV (con separador de campos), las comas serán sustituidas por el signo del separador especificado durante la apertura del archivo (si el separador no está especificado, por defecto se utiliza la tabulación). Al abrir el archivo en el modo FILE_TXT sin especificar el separador, el resultado va a ser idéntico al uso del signo "+" y el signo ",":

//--- escribir en el primer archivo
int h=FileOpen("1.txt",FILE_WRITE|FILE_ANSI|FILE_CSV);
FileWrite(h,"1","2","3");
FileClose(h);
//--- escribir en el segundo archivo
h=FileOpen("2.txt",FILE_WRITE|FILE_ANSI|FILE_CSV);
FileWrite(h,"1"+"2"+"3");
FileClose(h);
//--- escribir en el tercer archivo
h=FileOpen("3.txt",FILE_WRITE|FILE_ANSI|FILE_TXT);
FileWrite(h,"1","2","3");
FileClose(h);
//--- escribir en el cuarto archivo
h=FileOpen("4.txt",FILE_WRITE|FILE_ANSI|FILE_TXT);
FileWrite(h,"1"+"2"+"3");
FileClose(h);

Después de la ejecución de este código, el archivo "1.txt" va a contener "1    2    3", y el archivo "2.txt" va a contener "123" (los archivos han sido abiertos en el modo FILE_CSV). Los archivos "3.txt" y "4.txt" tienen el contenido idéntico: "123" (abiertos en el modo FILE_TXT). En este artículo no pretendemos entrar en los detalles de trabajo con los archivos. Por eso, si en el último ejemplo algo no le queda de todo claro, no se preocupe. Eso no le impedirá comprender el material expuesto a continuación. Simplemente tome en consideración que el uso de los signos "+" y "," para la concatenación de las cadenas no siempre produce el mismo efecto.

Además del uso del signo "+", en el lenguaje MQL5 existen unas funciones especializadas para concatenar las cadenas: StringAdd() y StringConcatenate(). De acuerdo con la descripción de estas funciones en el Manual de Referencia, ellas permiten unir las cadenas de forma más rápida y de manera más económica en cuanto a la memoria operativa ocupada. La función StringAdd() permite concatenar dos cadenas:

string str1,str2,str3;
//--- asignar valores
str1="La programación";
str2="en MQL5";
str3="para MetaTrader 5";
//--- llamar a la función de concatenación de cadenas
StringAdd(str1," ");
StringAdd(str1,str2);
StringAdd(str1," ");
StringAdd(str1,str3);
//--- mostrar resultado
Alert(str1);

Después de la ejecución de este código la variable str1 va a contener la cadena "Programación en MQL5 para MetaTrader 5".

La funciín StringConcatenate() permite concatenar varias cadenas a la vez. El primer parámetro que se pasa a la función es la variable de la cadena a la que van a unirse las cadenas listadas a continuación. La función puede aceptar el máximo de 64 parámetros:

string str1,str2,str3;
//--- asignar valores
str1="La programación";
str2="en MQL5";

str3="para MetaTrader 5";
//--- llamar a la función para concatenar varias cadenas
StringConcatenate(str1,str1," ",str2," ",str3);
//--- mostrar resultado
Alert(str1);

Después de la ejecución de este código, la variable str1 también va a contener la cadena "Programación en MQL5 para MetaTrader 5".

 

Conversión de diversas variables a una cadena

Cuando generamos una cadena del mensaje, muy a menudo surge la necesidad de añadir los valores de variables numéricas. Para convertir los valores de las variables de números enteros (char, uchar, bool, short, ushort, int, uint, color, long, ulong, datetime) a una cadena, se utiliza la función IntegerToString():

int x=1;
string str="x = "+IntegerToString(x);

Cuando se convierte una variable del tipo bool, se devuelve la cadena "0" (false) o "1" (true). Cuando se convierte una variable del tipo color o datetime, se devuelve la cadena con una expresión numérica del color o fecha/hora (por ejemplo, "65535" para el amarillo clrYellow o "1325376000" para la fecha 2012.01.01 00:00).

Para convertir las variables del tipo real (double, float) a una cadena, se utiliza la función DoubleToString(). El segundo parámetro de esta función determina la precisión (número de dígitos tras la coma):

double x=1.23456;
string str1="x = "+DoubleToString(x,2);
string str2="x = "+DoubleToString(x,3);

Después de la ejecución de este código, la variable str1 va a contener la cadena "1.23", y la variable str2 va a contener la cadena "1.235". El truncamiento hasta el número de dígitos especificado funciona según el principio del redondeo matemático.

Para convertir la fecha y la hora a una cadena del formato estándar (comprensible para una persona), se utiliza la función TimeToString():

datetime tm=TimeCurrent(); // Fecha/hora actual 
string str1=IntegerToString(tm);
string str2=TimeToString(tm);

Después de la ejecución de este código, la variable str1 va a contener la cadena con la expresión numérica de la hora (número de segundos transcurridos desde el 1 de enero de 1970), y la variable str2 - la fecha/hora formateada, por ejemplo "2012.11.02 22:00" (año, mes, fecha, horas, minutos).

Al invocar la función TimeToString(), podemos especificar el formato para representar la fecha y la hora. Las opciones disponibles son las siguientes:

string str1="Fecha y hora con minutos: "+TimeToString(tm);
string str2="Sólo fecha: "+TimeToString(tm,TIME_DATE);
string str3="Sólo hora con minutos: "+TimeToString(tm,TIME_MINUTES);
string str4="Sólo hora con segundos: "+TimeToString(tm,TIME_SECONDS);
string str5="Fecha y hora con segundos: "+TimeToString(tm,TIME_DATE|TIME_SECONDS);

El lenguaje MQL5 ofrece un excelente recurso funcional. Se trata de la creación de las enumeraciones que se muestran en la ventana de las propiedades del programa en forma de las listas desplegables con el conjunto de opciones a elegir. Los valores de estas variables también pueden ser convertidos a una cadena. Para eso se utiliza la función EnumToString(). A continuación puede ver el código del script que muestra el trabajo de esta función:

//+------------------------------------------------------------------+
//| Crear enumeración                                                |
//+------------------------------------------------------------------+
enum EMode
  {
   OFF=0,
   Mode1 = 1,
   Mode2 = 2,
   Mode3 = 3 
  };
//+------------------------------------------------------------------+
//| Iniciar srcript                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
   EMode Value=1;
   //--- concatenación de cadenas
   string str="Al valor "+IntegerToString(Value)+" le corresponde el punto "+EnumToString(Value)+" de la enumeración EMode";
  //--- mostrar resultado
   Alert(str);
  } 
La misma posibilidad también existe para la conversión de las variables del color. Se puede convertir el valor del color a su nombre utilizando la función ColorToString():
color ColorValue=clrRed;
string str=ColorToString(ColorValue,true);

Después de ejecutar este código, la variable str almacenará la cadena "clrRed". Si establecemos el valor false para el segundo parámetro, la función va a devolver la cadena con los valores de los componentes RGB (rojo, verde, azul):

color ColorValue=clrRed;
string str=ColorToString(ColorValue,false);

En este caso la variable str va a contener la cadena "255,0,0". Si el color no es estándar (es decir, no forma parte del conjunto de colores web y consecuentemente no tiene nombre), la función ColorToString() devuelve la cadena con los valores de los componentes independientemente del valor del segundo parámetro.

Existe otro modo de convertir las variables, utilizando la conversión de tipos:

int x=123;
string str=(string)x;

Cuando una variable del tipo bool se convierte de este modo, la cadena va a tener el valor " true" o "false": 

bool x=true;
string str=(string)x;

La conversión de las variables del tipo double y float se realiza con la máxima precisión posible, quitando sólo los ceros en la parte fraccionaria: 

double x1=0.1;
double x2=0.123string str1=(string)x1;
string str2=(string)x2;

Después de la ejecución de este código, la variable str1 va a guardar la cadena "0.1", y la variable str2 va a guardar la cadena "0.123".

 

Visualizar símbolos especiales

Cuando una variable string se inicializa por un valor, la cadena asignada debe ir encerrada entre comillas dobles para que el compilador pueda distinguir la cadena del código del programa. Para colocar las comillas dentro de la cadena, hay que indicar que en este caso las comillas no se utilizan para su finalidad común (como el signo que separa la cadena del código), sino se utiliza como una parte de la cadena. Para eso hay que colocar la barra inversa "\" antes del signo de comillas:

string str1="Texto simple";
string str2="\"Текст в кавычках\"";
//--- mostrar resultados
Alert(str1);
Alert(str2);

Puesto que la barra inversa también es un signo especial, para mostrarla dentro de la cadena es necesario colocar otra barra inversa delante de ella:

string str="\\";
Alert(str);

Después de ejecutar este código, el único signo que va a tener la cadena será "\".

Además, una cadena puede contener el signo de tabulación horizontal representado por "\t":

string str="Columna-1\tColumna-2\tColumna-3";
Alert(str);

En este caso la variable str va a contener la cadena "Columna-1        Columna-2        Columna-3".

El signo "\n" se utiliza para el cambio de línea. Es decir, se puede mostrar el texto en nuevas líneas:

string str="Línea-1\nLínea-2\nLínea-3";
Alert(str);

En este caso, el resultado de la ejecución de la función Alert() serán tres líneas del texto.

Cuando el texto se visualiza utilizando las funciones Alert() y MessageBox(), y también cuando se guarda en el archivo, Usted puede utilizar ambos signos: "\t" y "\n". Sin embargo, al visualizar en el comentario del gráfico (la función Comment()), se aplica sólo el signo del cambio de línea "\n", el signo de tabulación "\t" se ignora. Cuando el texto se visualiza utilizando la función Print(), el signo "\n" tampoco tiene efecto (cada parte de la cadena se muestra en una línea separada del diario), y el signo "\t" se sustituye por el espacio. En el log del archivo en el que se almacenan todos los mensajes a través de la función Print(), el signo "\t" también se sustituye por el espacio.

 

Formatear cadenas aplicando plantilla

Cuando damos formato a una cadena para visualizar, nos puede surgir la necesidad de incluir los valores de algunas variables numéricas en ella. Eso se puede conseguir a través de la concatenación de las cadenas y conversión de las variables numéricas a cadenas. Pero en este caso la cadena del código que forma el mensaje va a ser muy larga y difícil de comprender y editar en el caso de realizar mejoras del programa:

//--- inicialización de variables
int Variable1=1;
int Variable2=2;
int Variable3=3;
//--- adición larga de cadenas
string str="Variable1 = "+IntegerToString(Variable1)+", Variable2 = "+IntegerToString(Variable2)+", Variable3 = "+IntegerToString(Variable2);
//--- mostrar resultado
Alert(str);

Se puede resolver esta misma tarea de manera mucho más fácil utilizando la función StringFormat(). El primer parámetro pasado a la función será la plantilla del mensaje que tiene especificados los lugares de inserción de variables y definido el formato de su salida. A continuación, van enumeradas todas las variables en el orden en el que aparecen en la plantilla: 

//--- inicialización de variables
int Variable1=1;
int Variable2=2;
int Variable3=3;
//--- concatenación de cadenas mucho más simple
string str=StringFormat("Variable1 = %i, Variable2 = %i, Variable3 = %i",Variable1,Variable2,Variable3);
//--- mostrar resultado
Alert(str);

Los lugares de inserción de las variables se marcan con el signo "%" seguido por el símbolo "i" que en el ejemplo de arriaba significa que las variables tienen que salir como números enteros. Para ser más exacto, "i" significa las variables de números enteros con signos (char, short, int, color), mientras que para las variables de números enteros sin signos (uchar, bool, ushort, uint) se utiliza "u". Para las variables long, ulong, datetime también hay que especificar el número de bits de la variable poniendo "I64" antes del tipo:

string LongMin=StringFormat("%I64i",LONG_MIN);
string LongMax=StringFormat("%I64i",LONG_MAX);
string ULongMax=StringFormat("%I64u",ULONG_MAX);
string DateTimeMax=StringFormat("%I64u",DateMax);
//--- mostrar resultados
Alert("LongMin = "+LongMin);
Alert("LongMax = "+LongMax);
Alert("ULongMax = "+ULongMax);
Alert("DateTimeMax = "+DateTimeMax);
Como resultado del trabajo de este código, Usted verá la ventana emergente con los valores de las variables.

El formato de los números reales se denota por "f":
double Percents=5.5;
//--- número real como una cadena
string str=StringFormat("Percents = %f",Percents);
//--- mostrar resultado
Alert(str);

Después de ejecutar este código, la variable str almacenará la cadena "Percents = 5.500000", por defecto se muestran seis dígitos tras la coma. Se puede indicar el número de dígitos necesario:

string str=StringFormat("Percents = %.2f",Percents);

Para eso se pone el punto que significa el separador decimal y el número de dígitos después del separador. En este ejemplo son dos dígitos. En este caso la variable str va a contener la cadena "Percents = 5.50". Esta opción de formateo es completamente idéntica a la función DoubleToString().

Se puede indicar la longitud total del número. Para eso después del signo "%" se pone "0" y la cifra que determina la longitud del número, luego se pone la cantidad de dígitos tras la coma (si hace falta):

string str=StringFormat("Percents = %06.2f",Percents);

Aquí tenemos la longitud total de 6 dígitos de los cuales un dígito es el separador decimal y otros dos son dígitos tras la coma. Por eso la variable str va a contener la cadena "Percents = 005.50".

Si tenemos que mostrar el signo del porcentaje "%" en el mensaje, hay que ponerlo dos veces "%%" porque uno de ellos se utiliza para indicar el lugar de inserción de valores:

string str=StringFormat("Percents = %06.2f%%",Percents);

En este caso la variable str va a contener la cadena "Percents = 005.50%".

Además, Usted puede determinar la longitud del número cuando muestra las variables de números enteros:

int Variable=123;
//--- número entero como una cadena con longitud especificada
string str=StringFormat("Variable = %05i",Variable);
//--- mostrar resultado
Alert(str);

Después de ejecutar este código, la variable str almacenará la cadena "Variable = 00123".

Si la cantidad de dígitos especificada es menor que el mismo número, no importa, el número va a mostrarse de forma correcta:

string str=StringFormat("Variable = %02i",Variable); 

En este caso la variable str va a contener la cadena "Variable = 123". Es decir, va a mostrarse el número de tres dígitos a pesar de que se indique la longitud de 2.

Los números reales pueden mostrarse en el formato científico (la mantisa con seis dígitos tras el separador decimal y la potencia). Para eso se utiliza el signo "e":

double Variable=123.456;
//--- número real como una cadena en el formato científico
string str=StringFormat("Variable = %e",Variable);
//--- mostrar resultado
Alert(str);

Después de ejecutar este código, la variable str almacenará la cadena "1.234560e+002". Se puede utilizar la mayúscula "E" cuyo efecto es similar a la "e". Lo único que en la cadena fomateada en vez de la "e" va a figurar la mayúscula "E".

Hay otra opción de formatear los números reales - "g". En este caso se visualizan sólo seis dígitos (excluyendo el separador decimal). Si la longitud de la parte entera del número supera seis dígitos, este número se visualiza en el formato científico:

double Variable1=12.3456789;
double Variable2=1234567.89;
//--- obtener números reales como cadenas utilizando "g"
string str1=StringFormat("Variable = %g",Variable1);
string str2=StringFormat("Variable = %g",Variable2);
//--- mostrar resultado
Alert(str1+" "+str2);

En este ejemplo la variable str1 va a guardar la cadena "12.3457", y la variable str2 va a guardar la cadena "1.23457e+006". Si se utiliza la mayúscula "G", todo funciona igual. Lo único que en vez de la minúscula "e" se visualiza la mayúscula "E".

La función StringFormat() permite transformar los formatos de representación de los números: convertir los números del sistema decimal al sistema octal o hexadecimal. Para convertir un número al sistema octal se utiliza "o":

int Variable=17;
//--- número real como una cadena en el sistema octal
string str=StringFormat("Variable = %o",Variable);
//--- mostrar resultado
Alert(str);

Después de ejecutar este código, la variable str almacenará la cadena "Variable = 21" (8*2+1=17).

Para convertir un número al sistema hexadecimal, se utiliza "x" o "X". Si se utiliza la minúscula "x", el número hexadecimal va a componerse de las minúsculas. Si se utiliza la mayúscula "X" - de las mayúsculas:

color Variable=clrBlue;
//--- número real como una cadena en el sistema hexadecimal
string str=StringFormat("Variable = %x",Variable);
//--- mostrar resultado
Alert(str);

Después de ejecutar este código, la variable str almacenará la cadena "Variable = ff0000".

Existe la posibilidad de conversión inversa de un número hexadecimal al decimal. Para eso se utiliza "d":

int Variable=0x0000ff;
//--- número real como una cadena en el sistema decimal
string str=StringFormat("Variable = %d",Variable);
//--- mostrar resultado
Alert(str);

Después de ejecutar este código, la variable str almacenará la cadena "Variable = 255".

Para visualizar las variables de cadena se utiliza "s":

string Variable="texto";
//--- visualizar variable de cadena
string str=StringFormat("Variable = %s",Variable);
//--- mostrar resultado
Alert(str);

Después de ejecutar este código, la variable str almacenará la cadena "Variable = texto".

A veces surge la necesidad de alinear los números a la hora de mostrarlos en columna porque los números negativos se desplazan debido al signo "-". Para desplazar un número positivo, hay que agregar un espacio al inicio de la cadena. Para eso se inserta un espacio inmediatamente después de "%". En este caso los números negativos van a mostrarse sin espacio, y los positivos con un espacio al principio:

int Variable1=1;
int Variable2=-1;
//--- representación de los números con alineación
string str1=StringFormat("Variable1=% 03i",Variable1);
string str2=StringFormat("Variable2=% 03i",Variable2);
//--- mostrar resultados
Alert(str1);
Alert(str2);

Después de la ejecución de este código, la variable str1 va a guardar la cadena "Variable1= 01" (con espacio), y la variable str2 va a guardar la cadena "Variable2=-01".

Hay dos funciones más que son análogas a la función StringFormat(). Son las funciones PrintFormat() y printf() que tienen el mismo efecto y su única diferencia de la función StringFormat() consiste en que éstas dos muestran el texto en el diario como la función Print().

En realidad, la función StringFormat() tiene más posibilidades funcionales. Aquí hemos mostrado el mínimo necesario para ejecutar la mayoría de las tareas necesaria para formatear los números para la salida.
 

Mensajes en diferentes idiomas

La función StringFormat() permite dotar el programa con una posibilidad muy útil que consiste en mostrar mensajes en diferentes idiomas dependiendo del idioma de la interfaz establecido en el terminal.

Para averiguar el idioma de la interfaz, llamamos a la función TerminalInfoString() con el identificador TERMINAL_LANGUAGE. Al ejecutar el programa, preparamos la cadena de formateo dependiendo del idioma de la interfaz y luego la utilizamos en el programa. A continuación mostramos la plantilla del Asesor Experto con esta posibilidad:

//--- variable para la cadena de formateo
string FormatString;
//+------------------------------------------------------------------+
//| Procesamiento del evento Init                                    |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- obtener la cadena de formateo
   FormatString=GetFormatString();
//--- llamada adicional, para que el EA haga su trabajo por lo menos una vez durante el fin de semana si Usted lo desea
   OnTick();
   return(0);
  }
//+------------------------------------------------------------------+
//| Procesamiento del evento Tick                                    |
//+------------------------------------------------------------------+
void OnTick()
  {
   int Variable1,Variable2,Variable3;
   Variable1=MathRand()%10;        // Número aleatorio de 0 a 10
   Variable2=MathRand()%10;        // Otro número aleatorio
   Variable3=Variable1+Variable2; // Suma de números
//--- mostrar resultado
   Alert(StringFormat(FormatString,Variable1,Variable2,Variable3));
  }
//+------------------------------------------------------------------+
//| Determinar la cadena de formateo                                 |
//+------------------------------------------------------------------+
string GetFormatString(void)
  {
   string Language=TerminalInfoString(TERMINAL_LANGUAGE);
//--- comprobar idiomas
   if(Language=="Russian") return("%i плюс %i равно %i");     // ruso
   if(Language=="Spanish") return("%i más %i es igual a %i"); // español
//--- En todos los demás casos - inglés
   return("%i plus %i equals %i");
  }

El Asesor Experto suma dos números aleatorios y muestra el mensaje de texto sobre sus acciones, por ejemplo, "1 más 2 es igual a 3".

Eso es todo respecto a la salida de las cadenas. Ahora vamos a pasar a las manipulaciones con las cadenas más complejas pero más interesantes al mismo tiempo.

 

Principales funciones para trabajar con las cadenas

Si la cadena se introduce a través de la ventana de propiedades o se lee desde el archivo, puede contener espacios que sobran. Estos espacios pueden aparecer debido a la casual falta de cuidado del usuario, o tal vez así le haya sido más cómodo. Es deseable eliminar todos los espacios de los extremos antes de realizar alguna acción con la cadena. Para eso en el lenguaje MQL5 existen las funciones StringTrimLeft() (elimina espacios de la izquierda) y StringTrimRight() (elimina espacios de la derecha). Estas funciones eliminan no sólo los espacios, sino también los signos de tabulación y nueva línea. A la hora de trabajar con las cadenas, necesitamos eliminar muy a menudo los espacios de ambos lados a la vez, por eso sería muy útil escribir la función que lo va a hacer:

string Trim(string Str)
  {
   StringTrimLeft(Str);
   StringTrimRight(Str);
   return(Str);
  } 

Cuando se introducen los números reales, el usuario puede introducir una coma en vez del punto. Por esta razón, a la hora de introducir los números reales, será deseable garantizar la posibilidad de utilizar tanto el punto como la coma para el separador decimal. Para reemplazar una subcadena por otra, se utiliza la función StringReplace():

string str="123,456";
//--- reemplazar coma por punto
StringReplace(str,",",".");
double Value=StringToDouble(str);
//--- mostrar resultado
Alert(DoubleToString(Value));

Si no va a reemplazar "," por ".", entonces durante la transformación de la cadena en el número la parte fraccionaria va a ser truncada.

En algunas ocasiones puede surgir la necesidad de sustituir varios espacios seguidos por un espacio. Para eso primero hay que sustituir los signos de tabulación por el espacio, y luego sustituir dos espacios por uno hasta que quede sólo un espacio:

string str="Columna-1 \t Columna-2 \t Columna-3";
//--- sustituir tabulación por espacio
StringReplace(str,"\t"," ");
//--- obtener un espacio en vez de varios seguidos
while(StringReplace(str,"  "," ")>0){}
//--- mostrar resultado
Alert(str);

La función StringReplace() devuelve el número de sustituciones realizadas o -1 en caso del error. Por eso mientras que la función vaya devolviendo un valor mayor a cero, el ciclo continúa hasta que todos los pares de espacios queden sustituidos por un espacio. El cuerpo del ciclo no contiene ningún código. La función StringReplace() se invoca durante la comprobación de la condición del ciclo en cada iteración.

La función StringReplace() permite sustituir las subcadenas de distinta longitud:

string str="¡Programación en MQL5!";
//--- sustitución de subcadena, mostrar resultado
StringReplace(str,"en MQL5","para MetaTrader 5");
Alert(str);
//--- sustitución inversa, mostrar resultado
StringReplace(str,"para MetaTrader 5","en MQL5");
Alert(str);

Durante la ejecución de este código, tras la primera sustitución, la variable str va a contener la cadena "Programación para MetaTrader 5", y tras la segunda sustitución, de nuevo "¡Programación en MQL5 !".

La función StringFind() se utiliza para buscar la subcadena y devuelve el índice de la primera entrada de la subcadena en la cadena. El primer parámetro que se pasa a la función es la cadena donde se realiza la búsqueda, el segundo parámetro determina la subcadena a buscar, y el tercer parámetro (opcional) puede determinar la posición de inicio de búsqueda. Si el tercer parámetro no está especificado, la función trabaja como si su valor fuera igual a 0. Es decir, la búsqueda se realiza desde el inicio de la cadena. Vamos a buscar la posición de la subcadena "5" en la cadena "Programación en MQL5 para MetaTrader 5":

string str="Programación en MQL5 para MetaTrader 5";
//--- obtener la posición del carácter
int Pos=StringFind(str,"5");
//--- mostrar resultado
Alert(IntegerToString(Pos));

Después de ejecutar este código, la variable Pos tendrá el valor 23. En total la subcadena "5" se encuentra dos veces, pero la función ha devuelto sólo la posición de la primera ocurrencia. Si contamos la posición simplemente mirando la cadena, obtenemos 24. Es que la función empieza a contar a partir de cero y no desde uno. Si la subcadena que se busca no se encuentra en la cadena, la función devuelve -1.

A veces es necesario encontrar la posición de la última ocurrencia de la subcadena. Para eso tenemos que escribir una función personalizada: StringFindRev(). Vamos a buscar la primera ocurrencia de subcadena, luego desplazamos el inicio de la búsqueda de acuerdo con la posición encontrada, y así seguimos en el ciclo.

int StringFindRev(string Str,string Find)
  {
//--- variable pos para el valor devuelto
   int pos;
//--- variable auxiliar, se inicializa con el valor -1,
//--- para el caso si la subcadena no será encontrada dentro de la cadena
   int tmp=-1;
//--- ciclo. Será ejecutado por lo menos una vez
   do
     {
      //--- asignamos la última posición conocida de la subcadena
      pos=tmp;
      //--- seguimos buscando (se utiliza el tercer parámetro de la función)
      tmp=StringFind(Str,Find,tmp+1);
     }
   while(tmp!=-1); // Si en la parte restante de la cadena no hay subcadena, el ciclo 
                   // se finaliza y en la variable pos se queda la última
                   // posición conocida
//--- devolver la posición
   return(pos);
  }
Vamos a intentar utilizar esta función:
string str="Programación en MQL5 para MetaTrader 5";
//--- llamar a la función de búsqueda de la posición de la última ocurrencia del carácter en la cadena
int pos=StringFindRev(str,"5");
//--- mostrar resultado
Alert(pos);

Después de ejecutar este código, la variable Pos tendrá el valor 40.

La función StringSubstr() sirve para obtener la subcadena de longitud establecida desde la posición establecida. Vamos a obtener la subcadena con longitud 1 desde la posición 23:

string str="Programación en MQL5 para MetaTrader 5";
//--- obtener la subcadena desde la posición con longitud
string str2=StringSubstr(str,23,1);
//--- mostrar resultado
Alert(str2);

El resultado será "5".

Pues ahora, una vez aclaradas las funciones principales, vamos a utilizarlas para escribir una función bastante útil que sirve para eliminar una determinada lista de caracteres de la cadena. La función recibe la cadena fuente y la cadena que representa una lista de caracteres que necesitamos eliminar de la cadena fuente.

string TrimL(string Str,string List="\t\n ;")
  {
//--- variable para un carácter de la cadena Str
   string ch;
   int Len=StringLen(Str);
   int i=0;
//--- ciclo por todos los caracteres de la cadena Str
   for(;i<Len;i++)
     {
      //--- el siguiente carácter de la cadena Str
      ch=StringSubstr(Str,i,1);
      //--- si en la lista List no hay este carácter, la cadena tiene que empezarse desde esta posición 
      if(StringFind(List,ch,0)==-1)
        {
         break; // terminamos el trabajo del ciclo
        }
     }
//--- obtenemos la subcadena y la devolvemos
   return(StringSubstr(Str,i));
  }

Por defecto, la función elimina el signo de tabulación, nueva línea, espacio y punto y coma ";".

La misma función para eliminar del lado derecho:

string TrimR(string Str,string List="\t\n ;")
  {
//--- variable para un carácter de la cadena Str
   string ch;
   int Len=StringLen(Str);
//--- los caracteres en la cadena empiezan su numeración desde 0, por eso el último carácter tiene el índice a 1 menos que la longitud de la cadena
   int i=Len-1;
//--- ciclo por todos los caracteres de la cadena Str
   for(;i>=0;i--)
     {
      //--- el siguiente carácter de la cadena Str
      ch=StringSubstr(Str,i,1);
      //--- si en la lista List no hay este carácter, la cadena tiene que empezarse desde esta posición 
      if(StringFind(List,ch,0)==-1)
        {
         break; // terminamos el trabajo del ciclo
        }
     }
//--- obtenemos la subcadena y la devolvemos
   return(StringSubstr(Str,0,i+1));
  }

Por defecto, esta función también elimina el signo de tabulación, nueva línea, espacio y punto y coma ";". Esta función puede ser útil para leer desde los archivos CSV. Dentro de estos archivos, en el lado derecho de la cadena puede haber una gran cantidad de campos de separación (generalmente, se trata del punto y coma ";").

Para una persona la mayúscula y la minúscula (por ejemplo, "А" y "а" ) no suponen ninguna deferencia en cuanto a su sentido. Sin embargo, para el ordenador son dos símbolos completamente diferentes. Si ponemos "eurusd" en vez de "EURUSD" al solicitar los datos del mercado a través de la función SymbolInfoDouble(), la función no nos devolverá el valor necesario. Durante la introducción del símbolo a través de la ventana de propiedades la probabilidad de que ocurra este error es muy grande. Para cambiar entre mayúsculas y minúsculas, en el lenguaje MQL5 existen las funciones StringToLower() (pasar a minúsculas) y StringToUpper() (pasar a mayúsculas):

string str="EuRuSd";
string str1=str;
string str2=str;
//--- cambiar entre mayúsculas y minúsculas en las cadenas
StringToUpper(str1);
StringToLower(str2);
//--- mostrar resultado
Alert(str1," ",str2);

Después de la ejecución de este código, la variable str1 va a guardar la cadena "EURUSD", y la variable str2 va a guardar la cadena "eurusd".

Si necesita sólo comparar las cadenas sin tomar en consideración el formato de las letras, puede utilizar la función StringCompare(). Los primeros dos parámetros de esta función son las cadenas a comparar. El tercer parámetro determina si las cadenas tienen que compararse tomando en consideración el formato de las letras (true), o sin hacerlo (false):

int Result=StringCompare("eurusd","EURUSD",false);
Alert(Result); 

Si la función devuelve 0, entonces las cadenas son idénticas. Además, la función puede devolver -1 si la primera cadena es menor que la segunda, y 1 si la primera cadena es mayor que la segunda. "Mayor" y "menor" significa el estado de las cadenas cuando se ordenan alfabéticamente. La letra "b" es mayor que la "a":

int Result=StringCompare("a","b",true);
Alert(Result); 

En este caso la función devuelve -1.

Ahora, antes de continuar con otras funciones, es necesario hacer una breve digresión teórica.
  

Cadena desde el punto de vista del hombre y el ordenador

Qué es una cadena para una persona está bastante claro: es un texto formado por letras. Mientras que el ordenador, en comparación con la persona, es un dispositivo más simple que trata sólo con los números. El ordenador ve las imágenes, las cadenas y todo lo demás como los números. Una cadena es un array formado por números. A un carácter le corresponde un número (o mejor dicho, un código), a otro carácter, otro código, etc. Estos códigos se denominan los códigos ASCII (acrónimo inglés de American Standard Code for Information Interchange - Código Estándar Estadounidense para el Intercambio de Información). A continuación vamos a utilizar el término ASCII refiriéndose a su versión extendida "Extended ASCII" que contiene 256 códigos. De esta manera, podemos decir que el "alfabeto" del ordenador se compone de 256 caracteres. Pues bien, como diferentes pueblos e idiomas tienen sus propios alfabetos, para el ordenador también existen diferentes conjuntos de caracteres: páginas de códigos. Los usuarios de ordenadores en Rusia utilizan en su mayoría la página de códigos Windows-1251 que incluye las letras del alfabeto latino, alfabeto ruso, números, signos de puntuación y algunos otros símbolos. En la Fig. 1 podemos ver la página de códigos Windows-1251:


Fig. 1. Página de códigos Windows-1251.

Los primeros 32 caracteres no se muestran ya que se trata de los caracteres de control. Ellos no se muestran como tal, pero influyen en la visualización de los demás caracteres, por ejemplo, la tabulación (código 9), avance de línea (código 10), etc.

En los países de Europa Central se utiliza la codificación Windows-1250 (Fig. 2):


Fig. 2. Página de códigos Windows-1250.

Por favor, fíjense que en la página de códigos 1251 a partir del código 192 figuran las letras del alfabeto ruso, y en la página de códigos 1250 figuran así llamados signos diacríticos que se utilizan en los idiomas europeos (letras con marcas diacríticas que determinan pequeñas diferencias en la pronunciación del sonido).

256 caracteres es una cantidad muy pequeña. Los problemas surgen cuando tenemos que escribir un texto en diferentes idiomas, por ejemplo en ruso y francés (posee una gran cantidad de caracteres diacríticos) o en inglés y árabe (las letras se diferencian muchísimo de las letras de otros idiomas). Además, existe también el sistema de escritura jeroglífica (como por ejemplo en China, Japón) que incluye miles de jeroglíficos. En este caso las dificultades son aún más evidentes. Entonces tenemos que codificar de alguna otra manera los caracteres que no entran en la página de códigos. El que está familiarizado con el lenguaje HTML tiene que conocer la posibilidad de insertar los símbolos no estandarizados en las páginas html. Por ejemplo, el código &Agrave; se utiliza para mostrar el carácter À, &Aacute; para mostrar Á, etc.

Desde hace algún tiempo se ha generalizado bastante la codificación Unicode en la cual un carácter no se codifica con un byte (número de 0 a 255), sino con dos, llegando así a 65536 caracteres. Este conjunto de caracteres incluye todas las letras de todos los alfabetos del mundo, incluyendo los jeroglíficos más comunes, Fig. 3, (pero las fuentes correspondientes tienen que estar instaladas en su ordenador):

 Fig. 3. Letras de diferentes alfabetos y jeroglíficos.

Fig. 3. Letras de diferentes alfabetos y jeroglíficos.

En MQL5 las cadenas se codifican utilizando Unicode. Es decir, en la cadena un carácter puede representarse por un número de 0 a 65535. En las codificaciones ASCII y Unicode los caracteres con los códigos de 0 a 127 son los mismos. Los archivos de texto pueden contener el texto codificado con ASCII o Unicode, consecuentemente en el lenguaje MQL5 están previstas diferentes prestaciones funcionales para poder trabajar con las cadenas ASCII y las cadenas Unicode.

 

Convertir una cadena a un array y viceversa

Durante el trabajo con las cadenas, normalmente será suficiente utilizar las funciones StringLen(), StringFind(), StringSubst(), StringReplace() para resolver la mayoría de las tareas prácticas. Pero pueden haber tareas que se resuelven con más facilidad si trabajamos con las cadenas como con los números (por ejemplo, el cifrado, compresión de datos, cálculo de los números de control). Aunque no trabajamos a diario con este tipo de tareas, no podemos descartar que un día nos surja la necesidad de ocuparse de ellas. Existen las tareas más importantes cuya solución requiere que una cadena sea convertida a un array, tal como la transferencia de los parámetros de la cadena a las funciones Windows API (Application Programming Interfaces).

Para convertir una cadena a un array de los códigos Unicode, se utiliza la función StringToShortArray(), y para convertirla a un array ASCII, utilizamos la función StringToCharArray():

string str="MetaTrader 5";
//--- conversión de la cadena a un array Unicode
short sha[];
StringToShortArray(str,sha);
//--- conversión de la cadena a un array ASCII
uchar cha[];
StringToCharArray(str,cha);
//--- bandera de diferencia
bool Dif=false;
//--- comparando los arrays elemento por elemento
for(int i=0;i<StringLen(str);i++)
  {
   if(sha[i]!=cha[i])
     {
      Dif=true;
     }
  }
//--- mostrar resultado
if(Dif) Alert("Hay diferencias");
else    Alert("Idénticos");

Si los arrays obtenidos con el uso de las funciones StringToShortArray() y StringToCharArray() en el ejemplo de arriba son idénticos, se abre la ventana con el mensaje "Idénticos", y si hay diferencias, se muestra el mensaje "Hay diferencias". Para la cadena "MetaTrader 5" los arrays serán idénticos porque esta cadena se compone de los caracteres con los códigos hasta 127.

La cosa es un poco diferente para los códigos de caracteres superiores a 127. Los resultados del trabajo de la función StringToShortArray() siempre serán los mismos en cualquier lugar, mientras que los resultados del trabajo de la función StringToCharArray() van a depender de los ajustes regionales del sistema operativo.

En Windows 7 el idioma del sistema se puede seleccionar en Panel de Control - Configuración regional y de idioma - Administrativo - Cambiar configuración regional del sistema.

Vamos a ver un ejemplo. En la página de códigos 1251 al código 192 le corresponde la letra "А" (la primera letra del abecedario ruso), en la codificación 1250 a este código le corresponde la "Ŕ" (una de las letras más conocidas en el abecedario checo). Cuando se usa la función StringToShortArray(), la letra "А" siempre va a tener el código 1040, y la "Ŕ" - 340. Cuando se usa la función StringToCharArray(), si el sistema está configurado al Ruso, a la letra "А" le corresponde el código 192 (correcto), y a la "Ŕ" le corresponde 82 (la letra "R" del alfabeto latino). Pero si el sistema está configurado al Checo, a la "А" le corresponde el código 63 (signo de interrogación), y a la "Ŕ" - 192 (correcto). Las letras que contienen signos diactríticos se sustituyen por una letra parecida del alfabeto latino, y las letras menos comunes se sustituyen por el signo de interrogación.

Fíjense, por favor, en el tamaño de la cadena y de los arrays obtenidos:

int StrLen=StringLen(str);
int shaLen=ArraySize(sha);
int chaLen=ArraySize(cha);
//--- mostrar la longitud
Alert(StringFormat("%i, %i, %i",StrLen,shaLen,chaLen));

El array tiene un elemento más en comparación con el número de los caracteres en la cadena. Eso está relacionado con el hecho de que el final de la cadena se marca con el carácter con el código 0. Este carácter no se muestra en la cadena pero para el ordenador significa el final de la cadena mostrada. El intercambio de datos no siempre se realiza por un byte (carácter) en la cantidad correspondiente a la longitud de la cadena, pero en cualquier caso el carácter con el código 0 permite determinar el final de la cadena. Además, este cero puede crear problemas. Por ejemplo, durante el cifrado o el uso del algoritmo de compresión uno de los caracteres puede ser convertido al carácter con el código 0. En este caso, durante la conversión inversa del array a la cadena esta cadena puede quedarse incompleta. La solución de este problema requiere la aplicación de unos trucos especiales, pero este tema cae fuera del alcance de nuestro artículo.

La conversión inversa de un array a una cadena se realiza utilizando las funciones ShortArrayToString() y CharArrayToString():

//--- convertir el array de códigos Unicode a la cadena
short sha[]={85,110,105,99,111,100,101};
string str1=ShortArrayToString(sha);
//--- convertir el array de códigos ASCII a la cadena
uchar cha[]={65,83,67,73,73};
string str2=CharArrayToString(cha);
//--- mostrar resultados
Alert(str1+" "+str2);

Como resultado de ejecución de este ejemplo, la variable str1 va a guardar la cadena "Unicode", y la variable str2 va a guardar la cadena "ASCII".

Hay otras dos funciones parecidas: ShortToString() y CharToString(). Estas funciones convierten una sola variable del tipo short o char a una cadena de un carácter. La función CharToString() tiene un importante valor práctico. El objeto gráfico OBJ_ARROW que permite visualizar diferentes elementos está anclado al eje de precios por la vertical y al eje de tiempo por la horizontal. Es decir, estos signos se desplazan durante el desplazamiento del gráfico. El uso del objeto gráfico OBJ_LABEL junto con la fuente Wingdings permite mostrar diferentes símbolos anclados a las coordenadas de la pantalla, lo que permite crear diferentes paneles informativos. Buscamos el símbolo necesario en la tabla de caracteres Wingdings, convertimos su código a la cadena y visualizamos la cadena obtenida utilizando el objeto gráfico OBJ_LABEL:

   ObjectCreate(0,"lbl",OBJ_LABEL,0,0,0);           // crear objeto gráfico LABEL
   ObjectSetInteger(0,"lbl",OBJPROP_XDISTANCE,100);   // establecer la coordenada X
   ObjectSetInteger(0,"lbl",OBJPROP_YDISTANCE,100);   // establecer la coordenada Y
   ObjectSetInteger(0,"lbl",OBJPROP_FONTSIZE,20);     // establecer tamaño
   ObjectSetString(0,"lbl",OBJPROP_FONT,"Wingdings"); // establecer la fuente de caracteres
   string Icon=CharToString(37);                   // 37 - campanilla
   ObjectSetString(0,"lbl",OBJPROP_TEXT,Icon);       // establecer el texto a mostrar

Como resultado de ejecución de este ejemplo, en el gráfico aparecerá el símbolo con la imagen de la campanilla. El símbolo va a quedarse inmóvil durante el desplazamiento del gráfico.
Otras dos funciones para trabajar con los códigos de caracteres son StringGetCharacter() y StringSetCharacter(). Ellas trabajan con los códigos Unicode. La función StringGetCharacter() permite obtener el código del carácter que se encuentra en una posición determinada en la cadena:

string str="L5";
//--- obtener el código Unicode para el carácter con una posición especificada
ushort uch1=StringGetCharacter(str,0);
ushort uch2=StringGetCharacter(str,1);
//--- mostrar resultado
Alert(StringFormat("%i, %i",uch1,uch2));

Como resultado de ejecución de este código, la variable uch1 guardará el valor 76, la variable uch2  guardará el valor 53.

La función StringSetCharacter() permite cambiar el código del carácter en una posición determinada, así como añadir un carácter al final de la cadena:

string str="MQ5";
//--- reemplazar el carácter en la posición especificada de la cadena por el carácter Unicode que corresponde al código pasado
StringSetCharacter(str,2,76);
Alert(str);
//--- añadir el carácter Unicode que corresponde al código pasado al final de la cadena
StringSetCharacter(str,3,53);
Alert(str);

Durante la ejecución de este código, primero la variable str guardará la cadena "MQL" en vez de "MQ5", y después, "MQL5".

 

Llamar a la función API

Algunas de las funciones API utilizan los parámetros string como sus variables. Por ejemplo, la función para el inicio de aplicaciones ajenas WinExec utiliza el array del tipo uchar como su primer parámetro:

#import "kernel32.dll"
int WinExec(uchar &Path[],int Flag);
#import 

Vamos a intentar ejecutar notepad.exe (bloc de notas), el programa estándar de Windows. Convertimos la ruta hacia el bloc de notas al array del tipo uchar:

string PathName="C:\\WINDOWS\\notepad.exe";
uchar ucha[];
StringToCharArray(PathName,ucha);
int x=WinExec(ucha,1); 

En consecuencia, tiene que abrirse el editor de texto Notepad.

Otro ejemplo del uso de los parámetros string en las funciones API es la función MessageBoxW que muestra un mensaje en la ventana utilizando la codificación Unicode. Por esta razón, vamos a pasar los arrays del tipo ushort como sus parámetros:

#import "user32.dll"
int MessageBoxW(int hWnd,ushort &szText[],ushort &szCaption[],int nType);
#import

Ahora usamos esta función para mostrar la ventana con el mensaje:

ushort arr[];
ushort capt[];
//--- conversión
StringToShortArray("Programación en MQL5 para MetaTrader 5",arr);
StringToShortArray("Mensaje",capt);
//--- mostrar mensaje
MessageBoxW(0,arr,capt,0);

Tras la ejecución de este código, aparece el cuadro con el mensaje "Programación en MQL5 para MetaTrader 5".

Cabe mencionar que el uso de los arrays del tipo ushort en este ejemplo no es obligatorio, y que Usted puede pasar directamente las cadenas como parámetros de la función:

#import "user32.dll"
int MessageBoxW(int hWnd,string szText,string szCaption,int nType);
#import
//+------------------------------------------------------------------+
//| Función del inicio del script                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   MessageBoxW(0,"Programación en MQL5 para MetaTrader 5","Mensaje",0);
  }

Al final obtenemos el mismo resultado como antes, más arriba. Sin embargo, no se puede usar los arrays del tipo uchar como parámetros de esta función para la visualización correcta del mensaje:

#import "user32.dll"
int MessageBoxW(int hWnd,uchar &szText[],uchar &szCaption[],int nType);
#import
//+------------------------------------------------------------------+
//| Función del inicio del script                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   uchar arr[];
   uchar capt[];
//--- conversión
   StringToCharArray("Programación en MQL5 para MetaTrader 5",arr);
   StringToCharArray("Mensaje",capt);
//--- mostrar mensaje
   MessageBoxW(0,arr,capt,0);
  }

El código será ejecutado libre de errores y aparecerá la ventana pero el mensaje dentro estará deformado.

Para los casos cuando resulta necesario representar una cadena en la codificación ASCII (por ejemplo, si ya tenemos el array del tipo uchar), existe la función gemela, MessageBoxA. Para una visualización correcta, la función tiene que recibir sólo los arrays del tipo uchar como los parámetros string. Importamos esta función y la llamamos para mostrar el mensaje:

#import "user32.dll"
int MessageBoxA(int hWnd,uchar &szText[],uchar &szCaption[],int nType);
#import
//+------------------------------------------------------------------+
//| Función del inicio del script                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   uchar arr[];
   uchar capt[];
//--- conversión
   StringToCharArray("Programación en MQL5 para MetaTrader 5",arr);
   StringToCharArray("Mensaje",capt);
//--- mostrar mensaje
   MessageBoxA(0,arr,capt,0);
  }

Y obtenemos de nuevo el mensaje "Programación en MQL5 para MetaTrader 5".

Básicamente, para muchas funciones WinAPI que trabajan con las cadenas hay dos opciones: para trabajar con las cadenas ASCII y para trabajar con las cadenas Unicode.

Para poder invocar las funciones, hay que permitir el uso de dll en el terminal (Terminal - Menú de inicio - Herramientas - Opciones - Asesores Expertos - Permitir importación de DLL); o bien, al iniciar el script, EA o indicador, hay que marcar la opción "Permitir importación de DLL" en la ventana de propiedades, en la pestaña "Dependencias". Para habilitar la apertura de la ventana de propiedades del script, tiene que especificar la propiedad correspondiente del script:

#property script_show_inputs

 

Entrada ilimitada de parámetros

En la ventana de propiedades el usuario introduce la lista de parámetros separándolos con punto y coma:

input string Lots="0.1; 0.2; 0.3; 0.5";

Necesitamos convertir esta cadena a un array de variables del tipo double.

En el lenguaje MQL5, para dividir una cadena en un array, se utiliza la función StringSplit(). El primer parámetro que se pasa a la función es la cadena, el segundo será el código ASCII del símbolo que sirve del separador, el tercer parámetro será el array en el que va a almacenarse el resultado del trabajo de la función. Existe una manera muy simple para determinar el código ASCII: hay que encerrar el carácter necesario entre comillas simples.

int Code='A';
Alert(IntegerToString(Code)); 

Después de ejecutar este código, la variable Code guardará el valor 65 - código ASCII de la letra "A" del alfabeto romano. 

Vamos a presentar la solución de este problema como una función separada para poder usarla fácilmente en el momento necesario. El primer parámetro que pasamos a la función será la cadena, el segundo parámetro será el array que va a devolverse por referencia. El código de abajo incluye bastantes comentarios detallados y no creo que necesite explicaciones adicionales:

int ParamsToArray(string Str,double &Params[])
  {
//--- eliminar espacios de los extremos
   StringTrimLeft(Str);
   StringTrimRight(Str);
//--- si la cadena es vacía
   if(StringLen(Str)==0)
     {
      ArrayFree(Params); // liberación del array
      return(0);         // finalización del trabajo de la función
     }
//--- array auxiliar
   string tmp[];
//--- división de cadena
   int size=StringSplit(Str,';',tmp);
//--- eliminar espacios de los extremos para cada elemento del array
   for(int i=0;i<size;i++)
     {
      StringTrimLeft(tmp[i]);
      StringTrimRight(tmp[i]);
     }
//--- eliminar elementos vacíos del array (el usuario podría introducir 
//--- casualmente el separador dos veces seguidos o al final de la cadena)
   for(int i=size-1;i>=0;i--)
     {
      if(StringLen(tmp[i])==0)
        {
         ArrayCopy(tmp,tmp,i,i+1);
         size--; // el tamaño del array ha reducido
        }
     }
//--- redimensión del array de acuerdo con el tamaño nuevo
   ArrayResize(tmp,size);
//--- reemplazo de comas por puntos
   for(int i=0;i<size;i++)
     {
      StringReplace(tmp[i],",",".");
     }
//--- preparación del array a devorlver
   ArrayResize(Params,size);
//--- conversión de todos los elementos al tipo double y relleno del array a devolver 
   for(int i=0;i<size;i++)
     {
      Params[i]=StringToDouble(tmp[i]);
     }
//--- la función devuelve el número de parámetros
   return(size);
  }

 

Conversión de la cadena a diferentes variables

En la función ParamsToArray() se utilizaba la función estándar StringToDouble() para convertir la cadena a la variable del tipo double. Esta misma función se utiliza también para la conversión al tipo float. Además, existen las funciones estándar para la conversión a otros tipos de variables.

La función StringToInteger() convierte una cadena a la variable de números enteros:

string Str="12345.678";
//--- convertir la cadena a un número entero
long Val=StringToInteger(Str);
//--- conversión inversa y mostrar resultado
Alert(IntegerToString(Val));

Como resultado de ejecución de este código, la variable Val guardará el valor 12345. La parte fraccionaria simplemente se corta.

La función StringToTime() convierte una expresión string del tiempo a su correspondiente valor numérico. Se puede omitir el tiempo: el valor por defecto será "00:00":

string Str1="2012.11.02 22:00";
string Str2="2012.01.01";
//--- conversión de una expresión string del tiempo al tipo datetime
datetime DateTime1=StringToTime(Str1);
datetime DateTime2=StringToTime(Str2);

La función StringToColor() permite convertir el nombre del color (color web estándar) a su correspondiente valor numérico, o convertir la cadena de componentes RGB:

string Str1="clrYellow";
string Str2="255,255,0";
color Color1=StringToColor(Str1);
color Color2=StringToColor(Str2); 

Existe otro modo de convertir las cadenas al tipo de tiempo y color. Podemos usar este modo cuando asignamos determinados valores a las variables:

datetime DateTime=D'2012.11.02 22:00';
color Color=C'255,255,0'; 

Antes de la expresión string de la fecha se pone la letra "D" y la fecha se encierra entre comillas simples. Antes de la expresión string del color se pone la letra "С", y entre comillas simples van los componentes RGB separados con coma.

 

Habilitar notificaciones

El usuario introduce en la cadena una serie de caracteres que activan uno u otro tipo de notificación. De esta manera, tiene la posibilidad de habilitar diferentes combinaciones de notificaciones. A la habilitación de la alerta le corresponde la letra "а", sonido - "s", e-mail - "e", push - "p". Además de eso, se puede añadir la cifra 1 o 0 en la cadena para indicar la barra en la que se verifican las señales (puede ser útil para el uso en los indicadores). El código está representado como función cuyo primer parámetro que se pasa es la cadena, luego se devuelve por referencia la variable Shift (número de la barra) y las variables del tipo bool que corresponden a diferentes modos de notificación. El código tiene detallados comentarios:

void NotifyOnOff(string Str,int &Shift,bool &Alerts,bool &Sounds,bool &EMail,bool &Push)
  {
//--- Convertimos la cadena a minúsculas para que el usuario
//--- pueda utilizar tanto las minúsculas como las mayúsculas.
   StringToLower(Str);
//--- buscar letras en la cadena
   Alerts=(StringFind(Str,"a")!=-1);    // hay la "a"
   Sounds=(StringFind(Str,"s")!=-1);    // hay la "s"
   EMail=(StringFind(Str,"e")!=-1);     // hay la "e"
   Push=(StringFind(Str,"p")!=-1);      // hay la "p"
//--- buscar cero
   if(StringFind(Str,"0")!=-1) Shift=0;  // la cadena contiene "0"
   else                       Shift=1; // por defecto
  }

Ahora en vez de cinco variables en la ventana de propiedades es suficiente sólo una. 

 

Búfer de cadenas

Nos quedan por analizar tres funciones estándar: StringInit(), StringFill() y StringBufferLen().

La función StringInit() llena la cadena con caracteres idénticos de cantidad especificada:

string str;
StringInit(str,10,'|');
Alert(str); 

Después de ejecutar este código, la variable str almacenará la cadena "||||||||||". Para especificar un carácter, se pasa su código ASCII. Es decir, tenemos que encerrar el carácter entre comillas simples.

La función StringFill() llena la cadena con caracteres idénticos sin alterar su tamaño. A continuación del ejemplo anterior:

StringFill(str,'/');
Alert(str); 

Después de eso, a la variable str le va a corresponder la cadena "//////////".

Ahora vamos a intentar llenar la cadena con el carácter con el código 0 (final de la cadena):

StringFill(str,0); 
Comprobamos el tamaño de la cadena:
int Length=StringLen(str);
Alert(IntegerToString(Length)); 

El tamaño es igual a cero, el carácter con el código 0 se encuentra en la posición inicial de la cadena. Comprobamos el tamaño del búfer:

int BLength=StringBufferLen(str);
Alert(IntegerToString(BLength)); 

El tamaño del búfer es diferente de cero e incluso supera el tamaño de la cadena inicial. La memoria para la cadena es más que suficiente. Ahora, cuando a la cadena se le asigna un valor dentro del límite del tamaño del búfer, la redistribución de la memoria operativa para la cadena no es necesaria y la asignación se realizará muy rápido. Para que el tamaño del búfer no se reduzca, es necesario añadir nuevo valor en vez de asignarlo a la cadena:

str+="a"; 

Ahora la longitud de la cadena es 1, y el tamaño del búfer no ha cambiado De esta manera podemos acelerar un poco el procesamiento de las cadenas. Para limpiar la cadena, insertamos el carácter con el código 0 al principio.

StringSetCharacter(str,0,0); 

 

Conclusión

Podía pensarse que el tema de este artículo es de menor importancia que y sólo medianamente concierne al principal propósito del lenguaje MQL5, es decir, la programación de los Asesores Expertos e Indicadores. Sin embargo, ha salido un artículo bastante extenso ya que el lenguaje ofrece amplias prestaciones funcionales para trabajar con las cadenas. Incluso hay algo de probabilidad que en muchas ocasiones durante la programación de Asesores Expertos e Indicadores no tenga que trabajar con las cadenas, pero algún día tendrá que hacerlo. Después de leer este artículo, Usted estará totalmente preparado, y en caso de necesidad no tendrá que perder el tiempo para estudiar las funciones, simplemente podrá ponerse a hacer lo que necesita.

Vamos a repasar un poco el material expuesto en este artículo clasificando las funciones por categorías en función de su finalidad, importancia y frecuencia de uso.

  1. StringLen(), StringFind(), StringSubstr(), StringReplace(), StringSplit() son funciones esenciales y las más importantes que se utilizan para determinar la longitud de la cadena, buscar la subcadena, extraer la subcadena, sustituir la subcadena, dividir la cadena.
     
  2. StringTrimLeft(), StringTrinRight(), StringToLower(), StringToUpper() son funciones auxiliares de gran utilidad que sirven para eliminar espacios en los extremos y para cambiar entre mayúsculas y minúsculas.
     
  3. ColorToString(), DoubleToString(), EnumToString(), IntegerToString(), TimeToString(), StringFormat() son funciones para convertir las variables numéricas a la cadena.
     
  4. StringToColor(), StringToDouble(), StringToInteger(), StringToTime(), StringCompare() son funciones para convertir la cadena a las variables numéricas.
     
  5. StringAdd(), StringConcatenate() son funciones que se utilizan para unir y concatenar las cadenas.
     
  6. ShortToString(), ShortArrayToString(), StringToShortArray() и CharToString(), CharArrayToString(), StringToCharArray() son funciones para trabajar con las cadenas como con arrays. Pueden ser útiles a la hora de solucionar problemas que requieren manipulaciones complejas con las cadenas. Entre ellas hay que destacar dos funciones de mayor importancia: 

    • CharToString() se utiliza para trabajar con los objetos gráficos junto con la fuente Wingdings,
    • CharArrayToString() se utiliza para preparar el parámetro string durante la llamada a las funciones API.

  7. StringSetCharacter(), StringGetCharacter() и StringInit(), StringFill(), StringBufferLen() son funciones secundarias.

 

Archivos adjuntos

  1. IncStrFunctions.mqh - archivo con las funciones Trim(), StringFindRev(), TrimL(), TrimR(), ParamsToArray(), NotifyOnOff().
  2. eMultiLanguageMessage.mq5 es un ejemplo del EA con mensajes en viarios idiomas.

Traducción del ruso hecha por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/ru/articles/585

SQL y MQL5: Trabajando con la base de datos SQLite SQL y MQL5: Trabajando con la base de datos SQLite

El presente artículo va dirigido a programadores a los que les interesa el uso de SQL en sus proyectos. En el mismo, presentamos a los lectores la funcionalidad de SQLite y sus ventajas. El artículo no exige de conocimientos previos de SQLite, pero si sería de agradecer un conocimiento mínimo de SQL.

Creación de filtros digitales sin retardo temporal Creación de filtros digitales sin retardo temporal

En el presente artículo se estudia una de las aproximaciones para determinar la utilidad de una señal (tendencia) de flujo de datos. Algunos tests para el filtrado (suavización) de las cotizaciones de la bolsa, bastante útiles, demuestran la posibilidad potencial de crear filtros digitales (indicadores) que no sufran retrasos temporales y no se redibujen en las últimas barras.

Recetas MQL5 - Desarrollo de un indicador multidivisa para el análisis de la divergencia de precios Recetas MQL5 - Desarrollo de un indicador multidivisa para el análisis de la divergencia de precios

En este artículo veremos el desarrollo de un indicador multidivisa para el análisis de la divergencia de precios en un periodo de tiempo determinado. Ya hemos visto muchos momentos importantes en el anterior artículo sobre la programación de indicadores multidivisa: "Desarrollo de un indicador multidivisa de volatilidad en MQL5". Por eso, esta vez sólo nos detendremos en las funciones nuevas, o bien en aquellas funciones que hayan sufrido cambios significativos. Si es la primera vez que se encuentra con el tema de los indicadores multidivisa, entonces le recomendamos que lea en primer lugar el artículo anterior.

Recetas MQL5 - Asesor multidivisa y funcionamiento de órdenes pendientes en MQL5 Recetas MQL5 - Asesor multidivisa y funcionamiento de órdenes pendientes en MQL5

En esta ocasión veremos la creación de un asesor multidivisa, cuyo algoritmo de comercio será construido para trabajar con las órdenes pendientes Buy Stop y Sell Stop. En el artículo estudiaremos las siguientes cuestiones: el comercio en un diapasón temporal indicado, cómo establecer/modificar/eleminar órdenes pendientes, la comprobación de la última posición sobre Take Profit o Stop Loss y el control del historial de operaciones en cada símbolo.