Encontrar, reemplazar y extraer fragmentos de cadena
Quizá las operaciones más populares cuando se trabaja con cadenas sean encontrar y reemplazar fragmentos, así como extraerlos. En esta sección, estudiaremos las funciones de la API de MQL5 que ayudarán a resolver estos problemas. En el archivo StringFindReplace.mq5 se resumen ejemplos de su uso.
int StringFind(string value, string wanted, int start = 0)
La función busca la subcadena wanted en la cadena value, a partir de la posición start. Si se encuentra la subcadena, la función devolverá la posición donde comienza, con los caracteres de la cadena numerados empezando por 0. En caso contrario, la función devolverá -1. Ambos parámetros se pasan por valor, lo que permite procesar no sólo variables, sino también resultados intermedios de cálculos (expresiones, llamadas a funciones).
La búsqueda se realiza a partir de una coincidencia estricta de caracteres, es decir, distingue entre mayúsculas y minúsculas. Si desea buscar sin distinguir entre mayúsculas y minúsculas, primero debe convertir la cadena de origen a sólo mayúsculas o sólo minúsculas con StringToLoweroStringToUpper.
Intentemos contar el número de apariciones de la subcadena deseada en el texto utilizando StringFind. Para ello, vamos a escribir una función auxiliar CountSubstring que llamará a StringFind en un bucle, desplazando gradualmente la posición de inicio de la búsqueda en el último parámetro start. El bucle continuará en tanto se encuentren nuevas ocurrencias de la subcadena.
int CountSubstring(const string value, const string wanted)
|
Es importante señalar que la implementación presentada busca subcadenas que puedan solaparse. Esto se debe a que la posición actual se cambia en 1 (++cursor) antes de empezar a buscar la siguiente ocurrencia. Como resultado, al buscar, digamos, la subcadena «AAA» en la cadena «AAAAA» se encontrarán 3 coincidencias. Los requisitos técnicos de búsqueda pueden diferir de este comportamiento. En concreto, existe la práctica de continuar la búsqueda después de la posición en la que terminó el fragmento encontrado anteriormente. En este caso, será necesario modificar el algoritmo para que el cursor se desplace con un paso igual a StringLen(wanted).
Llamemos a CountSubstring para diferentes argumentos en la función OnStart.
void OnStart()
|
int StringReplace(string &variable, const string wanted, const string replacement)
La función sustituye todas las subcadenas wanted encontradas por la subcadena replacement de la cadena variable.
La función devuelve el número de sustituciones realizadas o -1 en caso de error. El código de error puede obtenerse llamando a la función GetLastError. En concreto, puede tratarse de errores de falta de memoria o del uso de una cadena no inicializada (NULL) como argumento. Los parámetros variables y wanted deben ser cadenas de longitud distinta de cero.
Cuando se da una cadena vacía "" como argumento replacement, todas las apariciones de wanted simplemente se cortan de la cadena original.
Si no hubo sustituciones, el resultado de la función es 0.
Vamos a utilizar el ejemplo de StringFindReplace.mq5 para comprobar StringReplace en acción.
string abracadabra = "ABRACADABRA";
|
A continuación, utilizando la función StringReplace, vamos a intentar ejecutar una de las tareas que se encuentran en el tratamiento de textos arbitrarios. Intentaremos que un determinado carácter separador se utilice siempre como un único carácter, es decir, que las secuencias de varios caracteres de este tipo se sustituyan por uno. Normalmente, se trata de espacios entre palabras, pero puede haber otros separadores en los datos técnicos. Vamos a probar nuestro programa para el separador '-'.
Implementamos el algoritmo como una función independiente NormalizeSeparatorsByReplace:
int NormalizeSeparatorsByReplace(string &value, const ushort separator = ' ')
|
El programa intenta reemplazar una secuencia de dos separadores por uno en un bucle do-while, y el bucle continúa siempre que la función StringReplace devuelve valores mayores que 0 (es decir, todavía hay algo que reemplazar). La función devuelve el número total de sustituciones realizadas.
En la función OnStart vamos a «limpiar» nuestra inscripción de múltiples caracteres '-'.
...
|
int StringSplit(const string value, const ushort separator, string &result[])
La función divide la cadena value pasada en subcadenas basadas en el separador dado y las coloca en el array result. La función devuelve el número de subcadenas recibidas o -1 en caso de error.
Si no hay separador en la cadena, el array tendrá un elemento igual a toda la cadena.
Si la cadena de origen está vacía o es NULL, la función devolverá 0.
Para demostrar el funcionamiento de esta función, vamos a resolver el problema anterior de una nueva forma utilizando StringSplit. Para ello, escribamos la función NormalizeSeparatorsBySplit.
int NormalizeSeparatorsBySplit(string &value, const ushort separator = ' ')
|
Cuando los separadores aparecen uno tras otro en el texto fuente, el elemento correspondiente en el array de salida StringSplit resulta ser una cadena vacía "". Además, una cadena vacía estará al principio del array si el texto comienza con un separador, y al final del array si el texto termina con el separador.
Para obtener un texto «despejado», hay que añadir todas las cadenas no vacías del array, «pegándolos» con caracteres separadores simples. Además, sólo deben convertirse en separadores aquellos elementos vacíos en los que el elemento anterior del array tampoco esté vacío.
Por supuesto, ésta es sólo una de las posibles opciones para implementar esta funcionalidad. Comprobémoslo en la función OnStart.
...
|
string StringSubstr(string value, int start, int length = -1)
La función extrae del texto pasado value una subcadena que comienza en la posición especificada start, de la longitud length. La posición inicial puede ser desde 0 hasta la longitud de la cadena menos 1. Si la longitud length es -1 o superior al número de caracteres desde start hasta el final de la cadena, el resto de la cadena se extraerá completa.
La función devuelve una subcadena o una cadena vacía si los parámetros son incorrectos.
Veamos cómo funciona.
PRT(StringSubstr("ABRACADABRA", 4, 3)); // 'CAD'
|