复制和移动文件
系统层级的主要文件操作是复制和移动。为此,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 组合,则使用逐位运算符 OR (|) 完成)。如果没有 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 脚本来学习如何实际使用这些函数。该脚本有两个文件名变量。当脚本运行时,两个文件均不存在。
const string source = "MQL5Book/source";
|
在 OnStart 中,我们根据一个简单场景执行一系列操作。首先,我们尝试将 source 文件从本地工作目录复制到总目录中的 destination 文件。如预期的那样,我们得到 false,并且 _LastError 中的错误代码将为 5019 (FILE_NOT_EXIST)。
void OnStart()
|
因此,我们将以常规方式创建一个源文件,写入一些数据,并将其刷写到磁盘。
int handle = PRTF(FileOpen(source, FILE_TXT | FILE_WRITE)); // 1
|
由于该文件仍然处于打开中,并且在打开时未指定 FILE_SHARE_READ 权限,以其它方式(绕过句柄)访问该文件仍然被阻止。因此,下一次复制尝试将再次失败。
PRTF(FileCopy(source, 0, destination, FILE_COMMON)); // false / CANNOT_OPEN_FILE(5004) |
我们来关闭文件并重试。但首先我们将生成文件的特性输出到日志:其创建时间和修改时间。两个特性均将包含你的计算机的当前时间戳。
FileClose(handle);
|
我们等待 3 秒,然后调用 FileCopy。这样你可以看到原始文件与其副本在特性方面的差异。这一暂停与之前对该文件的锁定无关:我们可以在关闭文件后立即复制,或者如果启用了 FILE_SHARE_READ 选项,甚至在写入它的同时进行复制。
Sleep(3000); |
我们复制文件。这次操作成功了。我们看看副本的特性。
PRTF(FileCopy(source, 0, destination, FILE_COMMON)); // true
|
每个文件有自己的创建时间(对于副本,创建时间比原文件迟 3 秒钟),但修改时间相同(副本继承了原始文件的特性)。
现在我们尝试将副本移动回本地文件夹。没有 FILE_REWRITE 选项便无法完成移动,因为没有覆写原始文件的权限。
PRTF(FileMove(destination, FILE_COMMON, source, 0)); // false / FILE_CANNOT_REWRITE(5020) |
通过更改该参数的值,我们将成功实现文件传输。
PRTF(FileMove(destination, FILE_COMMON, source, FILE_REWRITE)); // true |
最后,原始文件被删除,为此脚本的新测试留下一个干净的环境。
...
|