Tìm kiếm tệp và thư mục
MQL5 cho phép bạn tìm kiếm tệp và thư mục trong các sandbox của terminal, tester agent và sandbox chung cho tất cả các terminal (để biết thêm chi tiết về sandbox, xem phần giới thiệu chương Làm việc với tệp). Nếu bạn biết chính xác tên và vị trí của tệp/thư mục cần tìm, hãy sử dụng hàm FileIsExist
.
long FileFindFirst(const string filter, string &found, int flag = 0)
Hàm này bắt đầu tìm kiếm tệp và thư mục theo bộ lọc được truyền vào. Bộ lọc có thể chứa đường dẫn gồm các thư mục con trong sandbox và phải chứa tên chính xác hoặc mẫu tên của các phần tử hệ thống tệp cần tìm. Tham số filter
không được để trống.
Mẫu là một chuỗi chứa một hoặc nhiều ký tự đại diện. Có hai loại ký tự đại diện: dấu sao (*
) thay thế cho bất kỳ số lượng ký tự nào (bao gồm không ký tự), và dấu hỏi (?
) thay thế không quá một ký tự bất kỳ. Ví dụ, bộ lọc *
sẽ tìm tất cả tệp và thư mục, còn ???.*
sẽ chỉ tìm những tên có độ dài không quá 3 ký tự, và phần mở rộng có thể có hoặc không. Các tệp có phần mở rộng csv
có thể được tìm bằng bộ lọc *.csv
(nhưng lưu ý rằng thư mục cũng có thể có phần mở rộng). Bộ lọc *
. tìm các phần tử không có phần mở rộng, và .*
tìm các phần tử không có tên. Tuy nhiên, cần lưu ý điều sau:
Trong nhiều phiên bản Windows, hai loại tên được tạo cho các phần tử hệ thống tệp: tên dài (mặc định, tối đa 260 ký tự) và tên ngắn (định dạng 8.3 kế thừa từ MS-DOS). Tên ngắn được tạo tự động từ tên dài nếu nó vượt quá 8 ký tự hoặc phần mở rộng dài hơn 3 ký tự. Việc tạo tên ngắn có thể bị tắt trên hệ thống nếu không có phần mềm nào sử dụng chúng, nhưng chúng thường được bật.
Tệp được tìm kiếm trong cả hai loại tên, do đó danh sách trả về có thể chứa các phần tử bất ngờ thoạt nhìn. Đặc biệt, tên ngắn, nếu được hệ thống tạo từ tên dài, luôn chứa phần đầu trước dấu chấm, dài tối đa 8 ký tự. Nó có thể vô tình khớp với mẫu mong muốn.
Nếu bạn cần tìm tệp với nhiều phần mở rộng hoặc các đoạn khác nhau trong tên mà không thể tổng quát hóa bằng một mẫu, bạn sẽ phải lặp lại quá trình tìm kiếm nhiều lần với các cài đặt khác nhau.
Việc tìm kiếm chỉ được thực hiện trong một thư mục cụ thể (hoặc thư mục gốc của sandbox nếu không có đường dẫn trong bộ lọc, hoặc trong thư mục con được chỉ định nếu bộ lọc chứa đường dẫn) và không đi vào các thư mục con.
Tìm kiếm không phân biệt chữ hoa chữ thường. Ví dụ, yêu cầu tệp *.txt
cũng sẽ trả về các tệp có phần mở rộng TXT
, Txt
, v.v.
Nếu một tệp hoặc thư mục có tên phù hợp được tìm thấy, tên đó được đặt vào tham số đầu ra found
(yêu cầu một biến vì kết quả được truyền bằng tham chiếu) và hàm trả về một handle tìm kiếm: handle này cần được truyền cho hàm FileFindNext
để tiếp tục lặp qua các mục phù hợp nếu có nhiều mục.
Trong tham số found
, chỉ tên và phần mở rộng được trả về, không có đường dẫn (cấu trúc thư mục) có thể đã được chỉ định trong bộ lọc.
Nếu mục được tìm thấy là thư mục, ký tự \
(dấu gạch chéo ngược) sẽ được thêm vào bên phải tên của nó.
Tham số flag
cho phép chọn khu vực tìm kiếm giữa thư mục làm việc cục bộ của phiên terminal hiện tại (giá trị 0) hoặc thư mục chung của tất cả các terminal (giá trị FILE_COMMON
). Khi chương trình MQL được thực thi trong tester, sandbox cục bộ của nó (0) nằm trong thư mục tester agent.
Sau khi quá trình tìm kiếm hoàn tất, handle nhận được nên được giải phóng bằng cách gọi FileFindClose
(xem thêm bên dưới).
bool FileFindNext(long handle, string &found)
Hàm này tiếp tục tìm kiếm các phần tử hệ thống tệp phù hợp, được bắt đầu bởi hàm FileFindFirst
. Tham số đầu tiên là handle nhận được từ FileFindFirst
, nhờ đó tất cả các điều kiện tìm kiếm trước đó được áp dụng.
Nếu phần tử tiếp theo được tìm thấy, tên của nó được truyền cho mã gọi thông qua đối số found
, và hàm trả về true
.
Nếu không còn phần tử nào nữa, hàm trả về false
.
void FileFindClose(long handle)
Hàm này đóng handle tìm kiếm nhận được từ lời gọi FileFindFirst
.
Hàm phải được gọi sau khi quá trình tìm kiếm hoàn tất để giải phóng tài nguyên hệ thống.
Ví dụ, hãy xem xét script FileFind.mq5
. Trong các phần trước, chúng ta đã thử nghiệm nhiều script khác tạo tệp trong thư mục MQL5/Files/MQL5Book
. Yêu cầu danh sách tất cả các tệp như vậy.
void OnStart()
{
string found; // biến nhận kết quả
// bắt đầu tìm kiếm và lấy handle
long handle = PRTF(FileFindFirst("MQL5Book/*", found));
if(handle != INVALID_HANDLE)
{
do
{
Print(found);
}
while(FileFindNext(handle, found));
FileFindClose(handle);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Ngay cả khi bạn đã xóa thư mục này, bạn có thể sao chép các tệp mẫu đi kèm với sách ở nhiều mã hóa khác nhau vào đó. Vì vậy, script FileFind.mq5
nên xuất ra ít nhất danh sách sau (thứ tự liệt kê có thể thay đổi):
ansi1252.txt
unicode1.txt
unicode2.txt
unicode3.txt
utf8.txt
2
3
4
5
Để đơn giản hóa quá trình tìm kiếm, script có một hàm hỗ trợ DirList
. Nó chứa tất cả các lời gọi cần thiết đến các hàm tích hợp và một vòng lặp để xây dựng mảng chuỗi với danh sách các phần tử khớp với bộ lọc.
bool DirList(const string filter, string &result[], bool common = false)
{
string found[1];
long handle = FileFindFirst(filter, found[0]);
if(handle == INVALID_HANDLE) return false;
do
{
if(ArrayCopy(result, found, ArraySize(result)) != 1) break;
}
while(FileFindNext(handle, found[0]));
FileFindClose(handle);
return true;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
Với hàm này, chúng ta sẽ yêu cầu danh sách các thư mục trong sandbox cục bộ. Để làm điều này, chúng ta sử dụng giả định rằng thư mục thường không có phần mở rộng (trên lý thuyết, điều này không phải lúc nào cũng đúng, và do đó yêu cầu nghiêm ngặt hơn để liệt kê thư mục con nên được thực hiện khác bởi những ai muốn). Bộ lọc cho các phần tử không có phần mở rộng là *
. (bạn có thể kiểm tra bằng lệnh dir
trong shell Windows "dir *."). Tuy nhiên, mẫu này gây ra lỗi 5002 (WRONG_FILENAME
) trong các hàm MQL5. Do đó, chúng ta sẽ chỉ định một mẫu "mơ hồ" hơn *.?
: nó có nghĩa là các phần tử không có phần mở rộng hoặc có phần mở rộng 1 ký tự.
void OnStart()
{
...
string list[];
// thử yêu cầu các phần tử không có phần mở rộng
// (hoạt động trên dòng lệnh Windows)
PRTF(DirList("*.", list)); // false / WRONG_FILENAME(5002)
// mở rộng điều kiện: phần mở rộng phải không quá 1 ký tự
if(DirList("*.?", list))
{
ArrayPrint(list);
// ví dụ: "MQL5Book\" "Tester\"
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Trong phiên MetaTrader 5 của tôi, script tìm thấy hai thư mục MQL5Book\
và Tester\
. Bạn cũng nên có thư mục đầu tiên nếu bạn đã chạy các script thử nghiệm trước đó.