Копирование и перемещение файлов

Основные операции над файлами на уровне файловой системы — это копирование и перемещение. В MQL5 для этих целей реализованы две функции с одинаковыми прототипами.

bool FileCopy(const string source, int flag, const string destination, int mode)

Функция копирует исходный файл source в файл destination. Оба упомянутых параметра могут содержать исключительно имена файлов или имена вместе с предваряющими их путями (иерархиями папок) в "песочницах" MQL5. Параметры flag и mode определяют, соответственно, в какой рабочей папке ищется исходный файл и какая рабочая папка является целевой: значение 0 — это папка локального текущего экземпляра терминала (или агента тестирования, если программа запущена в тестере), а значение FILE_COMMON — общая папка для всех терминалов.

Кроме того, в параметре mode можно дополнительно указать константу FILE_REWRITE (если необходимо скомбинировать FILE_REWRITE и FILE_COMMON, это делается с помощью оператора побитового ИЛИ (|)). В отсутствие FILE_REWRITE копирование поверх существующего файла запрещено. Иными словами, если файл с путем и именем, указанным в параметре destination, уже существует, необходимо подтвердить намерение его переписать, установив FILE_REWRITE. Если этого не сделать, вызов функции завершится ошибкой.

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

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

При копировании файлов в них обычно сохраняются метаданные файловой системы (время создания, права доступа, альтернативные потоки данных). Если необходимо выполнить "чистое" копирование только данных самого файла, можно воспользоваться последовательными вызовами FileLoad и FileSave, см. раздел Запись и чтение файлов в упрощенном режиме.

bool FileMove(const string source, int flag, const string destination, int mode)

Функция перемещает или переименовывает файл. Исходные путь и имя указываются в параметре source, а целевые — в параметре destination.

Список параметров и принцип их действия — такие же, как для функции FileCopy. Грубо говоря, функция FileMove производит ту же работу, что и FileCopy, но дополнительно удаляет исходный файл после успешного копирования.

Приемы работы с функциями изучим на практике с помощью скрипта FileCopy.mq5. В нем заведено 2 переменных с именами файлов. Оба файла не существуют на момент запуска скрипта.

const string source = "MQL5Book/source";
const string destination = "MQL5Book/destination";

В OnStart произведем последовательность действий по простому сценарию. Для начала попробуем скопировать файл source из локального рабочего каталога в файл destination общего каталога. Мы ожидаемо получим результат false, а код ошибки в _LastError будет равен 5019 (FILE_NOT_EXIST).

void OnStart()
{
   PRTF(FileCopy(source0destinationFILE_COMMON)); // false / FILE_NOT_EXIST(5019)
   ...

Поэтому создадим исходный файл привычным способом, запишем некие данные и сбросим на диск.

   int handle = PRTF(FileOpen(sourceFILE_TXT | FILE_WRITE)); // 1
   PRTF(FileWriteString(handle"Test Text\n")); // 22
   FileFlush(handle);

Поскольку файл остался открытым и при открытии не было указано разрешение FILE_SHARE_READ, доступ к нему другими способами (минуя дескриптор) пока заблокирован. Поэтому следующая попытка копирования вновь завершится ошибкой.

   PRTF(FileCopy(source0destinationFILE_COMMON)); // false / CANNOT_OPEN_FILE(5004)

Закроем файл и повторим попытку. Но предварительно выведем в журнал свойства получившегося файла: когда он создан и модифицирован. Оба свойства будут содержать текущую метку времени на вашем компьютере.

   FileClose(handle);
   PRTF(FileGetInteger(sourceFILE_CREATE_DATE)); // 1629757115, пример
   PRTF(FileGetInteger(sourceFILE_MODIFY_DATE)); // 1629757115, пример

Выждем 3 секунды прежде чем вызывать FileCopy. Это позволит увидеть разницу в свойствах исходного файла и его копии. Эта пауза никак не связана с предыдущей блокировкой файла: мы могли бы выполнять копирование сразу после того, как закрыли файл, или даже в процессе его записи, если была бы включена опция FILE_SHARE_READ.

   Sleep(3000);

Скопируем файл — на этот раз удачно — и выведем свойства со временем для копии.

   PRTF(FileCopy(source0destinationFILE_COMMON)); // true
   PRTF(FileGetInteger(destinationFILE_CREATE_DATEtrue)); // 1629757118, +3 секунды
   PRTF(FileGetInteger(destinationFILE_MODIFY_DATEtrue)); // 1629757115, пример

Нетрудно заметить, что время создания у каждого файла свое (у копии оно на 3 секунды больше, чем у оригинала), а вот время модификации одинаковое (копия унаследовала свойство оригинала).

Теперь попробуем переместить копию обратно в локальную папку. Без опции FILE_REWRITE "этот номер не пройдет", потому что нет разрешения перезаписать исходный файл.

   PRTF(FileMove(destinationFILE_COMMONsource0)); // false / FILE_CANNOT_REWRITE(5020)

Изменив значение параметра, добьемся успешного переноса файла.

   PRTF(FileMove(destinationFILE_COMMONsourceFILE_REWRITE)); // true

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

   ...
   FileDelete(source);
}