Búsqueda de archivos y carpetas

MQL5 le permite buscar archivos y carpetas dentro de las «sandbox» de los terminales, los agentes probadores y la «sandbox» común para todos los terminales (para obtener más detalles sobre «sandboxes», consulte el capítulo de la introducción Trabajar con archivos). Si conoce exactamente el nombre y la ubicación del archivo/directorio requerido, utilice la función FileIsExist.

long FileFindFirst(const string filter, string &found, int flag = 0)

La función inicia la búsqueda de archivos y carpetas de acuerdo con el filtro pasado. El filtro puede contener una ruta formada por subcarpetas dentro del sandbox y debe contener el nombre exacto o el patrón de nombres de los elementos del sistema de archivos que se buscan. El parámetro filter no puede estar vacío.

Una plantilla es una cadena que contiene uno o varios caracteres comodín. Hay dos tipos de caracteres de este tipo: el asterisco ('*') sustituye a cualquier número de caracteres (incluido el cero) y el signo de interrogación ('?') no sustituye más que un carácter. Por ejemplo, el filtro «*» encontrará todos los archivos y carpetas, y «???.*» encontrará sólo aquellos cuyo nombre no tenga más de 3 caracteres, y la extensión puede o no estar presente. Los archivos con la extensión «csv» se pueden encontrar mediante el filtro «*.csv» (pero tenga en cuenta que la carpeta también puede tener una extensión). El filtro «*.» encuentra elementos sin extensión, y «.*» encuentra elementos sin nombre. No obstante, conviene recordar lo que se indica a continuación.

En muchas versiones de Windows se generan dos tipos de nombres para los elementos del sistema de archivos: largo (por defecto, hasta 260 caracteres) y corto (en el formato 8.3 heredado de MS-DOS). El segundo tipo se genera automáticamente a partir del nombre largo si éste supera los 8 caracteres o la extensión es superior a 3. Esta generación de nombres cortos puede desactivarse en el sistema si ningún software los utiliza, pero normalmente están activados.
 
Los archivos se buscan con ambos tipos de nombres, por lo que la lista devuelta puede contener elementos inesperados a primera vista. En particular, un nombre corto, si es generado por el sistema a partir de un nombre largo, contiene siempre una parte inicial antes del punto, de hasta 8 caracteres. Puede encontrar accidentalmente una coincidencia con el patrón deseado.

Si necesita encontrar archivos con varias extensiones, o con diferentes fragmentos en el nombre que no se pueden generalizar con un patrón, tendrá que repetir el proceso de búsqueda varias veces con diferentes configuraciones.

La búsqueda se realiza sólo en una carpeta específica (ya sea en la carpeta raíz de la «sandbox» si no hay ruta en el filtro, o en la subcarpeta especificada si el filtro contiene una ruta) y no entra en los subdirectorios.

La búsqueda no distingue entre mayúsculas y minúsculas. Por ejemplo, una solicitud de archivos «*.txt» también devolverá archivos con la extensión «TXT», «Txt», etc.

Si se encuentra un archivo o carpeta con un nombre coincidente, dicho nombre se coloca en el parámetro de salida found (requiere una variable porque el resultado se pasa por referencia) y la función devuelve un manejador de búsqueda: éste deberá pasarse a la función FileFindNext para continuar iterando sobre los elementos coincidentes si hay muchos.

En el parámetro found sólo se devuelven el nombre y la extensión, sin la ruta (jerarquía de carpetas) que pudiera haberse especificado en el filtro.

Si el elemento encontrado es una carpeta, se añade un carácter '\' (barra invertida) a la derecha de su nombre.

El parámetro flag permite seleccionar la zona de búsqueda entre la carpeta de trabajo local de la copia actual del terminal (por valor 0) o la carpeta común de todos los terminales (por valor FILE_COMMON). Cuando un programa MQL se ejecuta en un probador, su «sandbox» local (0) se encuentra en el directorio del agente del probador.

Una vez finalizado el procedimiento de búsqueda, el manejador recibido debe liberarse llamando a FileFindClose (ver más adelante).

bool FileFindNext(long handle, string &found)

La función continúa buscando elementos adecuados del sistema de archivos, iniciada por la función FileFindFirst. El primer parámetro es el descriptor recibido de FileFindFirst, debido al cual se aplican todas las condiciones de búsqueda anteriores.

Si se encuentra el siguiente elemento, su nombre se pasa al código de llamada a través del argumento found, y la función devuelve true.

Si no hay más elementos, la función devuelve false.

void FileFindClose(long handle)

La función cierra el descriptor de búsqueda recibido como resultado de la llamada FileFindFirst.

La función debe llamarse una vez finalizado el procedimiento de búsqueda para liberar recursos del sistema.

Como ejemplo, consideremos el script FileFind.mq5. En las secciones anteriores probamos muchos otros scripts que creaban archivos en el directorio MQL5/Files/MQL5Book. Solicite una lista de todos los archivos de este tipo.

void OnStart()
{
   string found// receiving variable
   // start searching and get descriptor 
   long handle = PRTF(FileFindFirst("MQL5Book/*"found));
   if(handle != INVALID_HANDLE)
   {
      do
      {
         Print(found);
      }
      while(FileFindNext(handlefound));
      FileFindClose(handle);
   }
}

Aunque haya vaciado este directorio, puede copiar en él los archivos de muestra suministrados con el libro en diversas codificaciones. Por lo tanto, el script FileFind.mq5 debería mostrar al menos la siguiente lista (el orden de enumeración puede cambiar):

ansi1252.txt
unicode1.txt
unicode2.txt
unicode3.txt
utf8.txt

Para simplificar el proceso de búsqueda, el script dispone de una función DirList auxiliar. Contiene todas las llamadas necesarias a las funciones integradas y un bucle para construir un array de cadenas con una lista de elementos que coinciden con el filtro.

bool DirList(const string filterstring &result[], bool common = false)
{
   string found[1];
   long handle = FileFindFirst(filterfound[0]);
   if(handle == INVALID_HANDLEreturn false;
   do
   {
      if(ArrayCopy(resultfoundArraySize(result)) != 1break;
   }
   while(FileFindNext(handlefound[0]));
   FileFindClose(handle);
   
   return true;
}

Con él, solicitaremos una lista de directorios en la «sandbox» local. Para ello, partimos de la base de que los directorios no suelen tener extensión (en teoría, no siempre es así y, por lo tanto, aquellos que lo deseen deberían implementar de forma diferente una solicitud más estricta de una lista de subcarpetas). El filtro para elementos sin extensión es «*» (puede comprobarlo con el comando dir en el shell «dir *.» de Windows ). Sin embargo, esta plantilla provoca el error 5002 (WRONG_FILENAME) en las funciones de MQL5. Por lo tanto, especificaremos una plantilla «*.?» más «vaga»: significa elementos sin extensión o con una extensión de 1 carácter.

void OnStart()
{
   ...
   string list[];
   // try to request elements without extension
   // (works on the Windows command line)
   PRTF(DirList("*."list)); // false / WRONG_FILENAME(5002)
   
   // expand the condition: the extension must be no more than 1 character
   if(DirList("*.?"list))
   {
      ArrayPrint(list);
      // example: "MQL5Book\" "Tester\"
   }
}

En mi instancia de MetaTrader 5, el script encuentra dos carpetas «MQL5Book\» y «Tester\». Debería tener también el primero si ha ejecutado los scripts de prueba anteriores.