Searching for files and folders

MQL5 allows you to search for files and folders within terminal sandboxes, tester agents, and the common sandbox for all terminals (for more details about sandboxes, see the chapter introduction Working with files). If you know exactly the required file/directory name and location, use the FileIsExist function.

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

The function starts searching for files and folders according to the passed filter. The filter can contain a path consisting of subfolders within the sandbox and must contain the exact name or name pattern of the file system elements that are searched for. The filter parameter cannot be empty.

A template is a string that contains one or more wildcard characters. There are two types of such characters: the asterisk ('*') replaces any number of any characters (including zero), and the question mark ('?') replaces no more than one of any character. For example, the filter "*" will find all files and folders, and "???.*" will find only those having the name no longer than 3 characters, and the extension may or may not be present. Files with the "csv" extension can be found by the "*.csv" filter (but note that the folder can also have an extension). Filter "*." finds elements without an extension, and ".*" finds elements without a name. However, the following should be remembered here.

In many versions of Windows, two kinds of names are generated for file system elements: long (by default, up to 260 characters) and short (in the 8.3 format inherited from MS-DOS). The second kind is automatically generated from the long name if it exceeds 8 characters or the extension is longer than 3. This generation of short names can be disabled on the system if no software uses them, but they are usually enabled.
 
Files are searched in both types of names, which is why the returned list may contain elements that are unexpected at first glance. In particular, a short name, if generated by the system from a long name, always contains an initial part before the dot, up to 8 characters long. It may accidentally find a match with the desired pattern.

If you need to find files with several extensions, or with different fragments in the name that cannot be generalized by one pattern, you will have to repeat the search process several times with different settings.

The search is performed only in a specific folder (either in the root folder of the sandbox if there is no path in the filter, or in the specified subfolder if the filter contains a path) and does not go into subdirectories.

The search is not case-sensitive. For example, a request for "*.txt" files will also return files with the extension "TXT", "Txt", etc.

If a file or folder with a matching name is found, that name is placed in the output parameter found (requires a variable because the result is passed by reference) and the function returns a search handle: this will need to be passed to the FileFindNext function to continue iterating over matching items if there are many.

In the found parameter, only the name and extension are returned, without the path (folder hierarchy) that might have been specified in the filter.

If the item found is a folder, a '\' (backslash) character is appended to the right of its name.

The flag parameter allows the selection of the search area between the local working folder of the current copy of the terminal (by value 0) or the common folder of all terminals (by value FILE_COMMON). When an MQL program is executed in a tester, its local sandbox (0) is located in the tester agent directory.

After the search procedure is completed, the received handle should be freed by calling FileFindClose (see further along).

bool FileFindNext(long handle, string &found)

The function continues searching for suitable elements of the file system, started by the FileFindFirst function. The first parameter is the descriptor received from FileFindFirst, due to which all the previous search conditions are applied.

If the next element is found, its name is passed to the calling code via the argument found, and the function returns true.

If there are no more elements, the function returns false.

void FileFindClose(long handle)

The function closes the search descriptor received as a result of the call FileFindFirst.

The function must be called after the search procedure is completed in order to free system resources.

As an example, let's consider the script FileFind.mq5. In the previous sections, we tested many other scripts that created files in the directory MQL5/Files/MQL5Book. Request a list of all such files.

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);
   }
}

Even if you have cleared this directory, you can copy the sample files supplied with the book in various encodings into it. So the script FileFind.mq5 should output at least the following list (the order of enumeration may change):

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

To simplify the search process, the script has an auxiliary function DirList. It contains all the necessary calls to built-in functions and a loop for building a string array with a list of elements that match the filter.

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;
}

With it, we will request a list of directories in the local sandbox. To do this, we use the assumption that directories usually do not have an extension (in theory, this is not always the case, and therefore a more strict request for a list of subfolders should be implemented differently by those who wish). The filter for elements with no extension is "*." (you can check it with the command dir in Windows shell "dir *."). However, this template causes error 5002 (WRONG_FILENAME) in MQL5 functions. Therefore, we will specify a more "vague" template "*.?": it means elements without an extension or with an extension of 1 character.

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\"
   }
}

In my MetaTrader 5 instance, the script finds two folders "MQL5Book\" and "Tester\". You should have the first one too if you ran the previous test scripts.