Работа с папками

Файловую систему трудно представить без способности структурировать хранимую информацию за счет произвольной иерархии каталогов — контейнеров для наборов логически связанных файлов. На уровне MQL5 данная возможность также поддерживается. При необходимости мы можем создавать, очищать и удалять папки с помощью встроенных функций FolderCreate, FolderClean, FolderDelete.

Ранее мы уже видели один способ создания папки, причем, возможно, даже не одной, а сразу всей требуемой иерархии вложенных папок: для этого достаточно при создании (открытии) файла с помощью FileOpen или при его копировании (FileCopy, FileMove) задать не просто имя, а предварить его требуемым путем. Например,

   FileCopy("MQL5Book/unicode1.txt"0"ABC/DEF/code.txt"0);

Эта инструкция создаст папку "ABC" в "песочнице", в ней — папку "DEF", и скопирует туда файл под новым именем (файл-источник должен существовать).

Если создавать заранее файл-источник не хочется, можно создать файл-пустышку на лету:

   uchar dummy[];
   FileSave("ABC/DEF/empty"dummy);

Здесь мы получим ту же иерархию папок, что и в предыдущем примере, но с файлом "empty" нулевого размера.

При подобных подходах создание папок становится как бы побочным продуктом работы с файлами. Однако иногда требуется оперировать папками, как самостоятельными сущностями и без побочных эффектов, в частности, просто создать пустую папку. Это позволяет сделать функция FolderCreate.

bool FolderCreate(const string folder, int flag = 0)

Функция создает папку с именем folder, которое может включать путь (несколько названий папок верхних уровней). В любом случае, одиночная папка или иерархия папок создается в "песочнице", определяемой параметром flag. По умолчанию, когда flag равен 0, используется локальная рабочая папка MQL5/Files терминала или агента тестирования (если программа запущена в тестере). Если flag равен FILE_COMMON, используется общая папка всех терминалов.

Функция возвращает true в случае успеха или если папка уже существует. В случае ошибки результат равен false.

bool FolderClean(const string folder, int flag = 0)

Функция удаляет все файлы и папки любого уровня вложенности (вместе со всем содержимым) в указанном каталоге folder. Параметр flag задает "песочницу" (локальную или общую), в которой производится действие.

Осторожно пользуйтесь этой функцией, так как все файлы и вложенные папки (с их файлами) удаляются безвозвратно.

bool FolderDelete(const string folder, int flag = 0)

Функция удаляет указанную папку (folder). Перед вызовом функции папка должна быть пуста, иначе её не удастся удалить.

Приемы работы с данными тремя функциями продемонстрированы в скрипте FileFolder.mq5. Вы можете выполнять данный скрипт в режиме отладки по шагам (инструкция за инструкцией) и наблюдать в файловом менеджере, как появляются и исчезают папки и файлы. Однако учтите, что перед выполнением очередной инструкции следует выходить файловым менеджером из созданных папок наверх до уровня "MQL5Book", потому что в противном случае папки могут быть заняты файловым менеджером, и это нарушит работу скрипта.

В начале мы создаем несколько вложенных папок как побочный продукт записи в них пустого фиктивного файла.

void OnStart()
{
   const string filename = "MQL5Book/ABC/DEF/dummy";
   uchar dummy[];
   PRTF(FileSave(filenamedummy)); // true

Далее мы создаем еще одну папку на нижнем уровне вложенности с помощью FolderCreate: на этот раз папка появляется сама по себе, без вспомогательного файла.

   PRTF(FolderCreate("MQL5Book/ABC/GHI")); // true

Если попытаться удалить папку "DEF", это закончится ошибкой, потому что она не пуста (там есть файл).

   PRTF(FolderDelete("MQL5Book/ABC/DEF")); // false / CANNOT_DELETE_DIRECTORY(5024)

Для того чтобы её удалить, необходимо её сперва очистить, и проще всего это сделать с помощью FolderClean. Но мы попробуем сымитировать часто встречающуюся ситуацию, когда некоторые файл в очищаемых папках могут быть заблокированы другими MQL-программами, внешними приложениями или самим терминалом. Откроем файл на чтение и вызовем FolderClean.

   int handle = PRTF(FileOpen(filenameFILE_READ)); // 1
   PRTF(FolderClean("MQL5Book/ABC")); // false / CANNOT_CLEAN_DIRECTORY(5025)

Функция возвращает false и выставляет код ошибки 5025 (CANNOT_CLEAN_DIRECTORY). После того как мы закроем файл, очистка и удаление всей иерархии папок проходит успешно.

   FileClose(handle);
   PRTF(FolderClean("MQL5Book/ABC")); // true
   PRTF(FolderDelete("MQL5Book/ABC")); // true
}

Потенциальные блокировки более вероятны при использовании общего каталога терминалов, где на один файл или папку могут "претендовать" разные экземпляры программ. Но и в локальной песочнице не стоит забывать о возможных конфликтах (например, если csv-файл открыт в Excel). Снабжайте фрагменты кода, работающего с папками, подробной диагностикой и выводом ошибок, чтобы пользователь смог заметить и устранить проблему.