处理文件夹

要是一个文件系统不能够通过任意式目录层次结构(逻辑相关文件集容器)来结构化存储的信息,那简直是不可思议。在 MQL5 层面,也支持该功能。如果必要,我们可以使用内置函数 FolderCreateFolderClean 以及 FolderDelete 来创建、清理和删除文件夹。

早前我们已经了解一种创建文件夹的方法,并且或许不只是创建一个文件夹,而是一次性创建要求的子文件夹的整个层次结构。为此,当使用 FileOpen创建(打开)文件,或者复制文件(FileCopyFileMove)时,不仅要指定名称,还要在名称前加上要求的路径。例如,

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

该语句将在沙盒中创建 "ABC" 文件夹,在该文件夹中创建 "DEF" 文件夹,并以新名称复制其中的文件(源文件必须存在)。

如果不想事先创建一个源文件,可以临时创建一个虚拟文件:

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

我们将获得与前面示例相同的文件夹结构,但获得一个零大小的“空”文件。

利用此类方法,文件夹的创建一定程度上成为处理文件的副产品。然而,有时候需要将文件夹作为独立实体进行操作且不产生附带影响,尤其是仅创建一个空文件夹。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 中打开)。应对处理文件夹的代码部分实现详细的诊断和错误输出功能,以便用户能够注意到问题并修复问题。