Chọn mã hóa cho chế độ văn bản
Đối với các tệp văn bản được ghi, mã hóa nên được chọn dựa trên đặc điểm của văn bản hoặc điều chỉnh theo yêu cầu của các chương trình bên ngoài mà các tệp được tạo ra hướng tới. Nếu không có yêu cầu bên ngoài, bạn có thể tuân theo quy tắc luôn sử dụng ANSI cho các văn bản thuần túy với số, chữ cái tiếng Anh và dấu câu (một bảng gồm 128 ký tự quốc tế như vậy được đưa ra trong phần So sánh chuỗi). Khi làm việc với nhiều ngôn ngữ khác nhau hoặc các ký tự đặc biệt, hãy sử dụng UTF-8 hoặc Unicode, tức là tương ứng:
int u8 = FileOpen("utf8.txt", FILE_WRITE | FILE_TXT | FILE_ANSI, 0, CP_UTF8);
int u0 = FileOpen("unicode.txt", FILE_WRITE | FILE_TXT | FILE_UNICODE);
2
Ví dụ, các cài đặt này hữu ích để lưu tên của các công cụ tài chính vào một tệp, vì đôi khi chúng sử dụng các ký tự đặc biệt biểu thị tiền tệ hoặc chế độ giao dịch.
Việc đọc các tệp của chính bạn không nên là vấn đề, vì chỉ cần chỉ định cùng cài đặt mã hóa khi đọc như khi bạn đã ghi. Tuy nhiên, các tệp văn bản có thể đến từ nhiều nguồn khác nhau. Mã hóa của chúng có thể không được biết trước, hoặc thay đổi mà không có thông báo trước. Do đó, câu hỏi đặt ra là phải làm gì nếu một số tệp được cung cấp dưới dạng chuỗi một byte (ANSI), một số dưới dạng chuỗi hai byte (Unicode), và một số dưới dạng mã hóa UTF-8.
Mã hóa có thể được chọn thông qua các tham số đầu vào của chương trình. Tuy nhiên, điều này chỉ hiệu quả với một tệp, và nếu bạn phải mở nhiều tệp khác nhau, mã hóa của chúng có thể không khớp. Vì vậy, mong muốn là hướng dẫn hệ thống đưa ra lựa chọn mô hình chính xác ngay lập tức (từ tệp này sang tệp khác).
MQL5 không cho phép phát hiện và áp dụng mã hóa chính xác một cách hoàn toàn tự động 100%, tuy nhiên, có một chế độ phổ biến nhất để đọc nhiều loại tệp văn bản khác nhau. Để làm điều này, bạn cần đặt các tham số đầu vào sau cho hàm FileOpen
:
int h = FileOpen(filename, FILE_READ | FILE_TXT | FILE_ANSI, 0, CP_UTF8);
Có một số yếu tố đang hoạt động.
Thứ nhất, mã hóa UTF-8 bỏ qua một cách minh bạch 128 ký tự đã đề cập trong bất kỳ mã hóa ANSI nào (tức là chúng được truyền "một đối một").
Thứ hai, nó là phổ biến nhất cho các giao thức Internet.
Thứ ba, MQL5 có phân tích tích hợp bổ sung cho định dạng văn bản trong Unicode hai byte, cho phép tự động chuyển chế độ hoạt động tệp sang FILE_UNICODE
nếu cần, bất kể các tham số được chỉ định. Thực tế là các tệp ở định dạng Unicode thường được bắt đầu bằng một cặp định danh đặc biệt: 0xFFFE
, hoặc ngược lại, 0xFEFF
. Chuỗi này được gọi là Byte Order Mark (BOM). Nó cần thiết vì, như chúng ta biết, các byte có thể được lưu trữ bên trong số theo thứ tự khác nhau trên các nền tảng khác nhau (điều này đã được thảo luận trong phần Kiểm soát Endianness trong số nguyên).
Định dạng FILE_UNICODE
sử dụng số nguyên 2 byte (mã) cho mỗi ký tự, vì vậy thứ tự byte trở nên quan trọng, không giống như các mã hóa khác. BOM thứ tự byte của Windows là 0xFFFE
. Nếu lõi MQL5 tìm thấy nhãn này ở đầu tệp văn bản, việc đọc nó sẽ tự động chuyển sang chế độ Unicode.
Hãy xem cách các cài đặt chế độ khác nhau hoạt động với các tệp văn bản có mã hóa khác nhau. Để làm điều này, chúng ta sẽ sử dụng script FileText.mq5
và một số tệp văn bản có cùng nội dung, nhưng ở các mã hóa khác nhau (kích thước tính bằng byte được ghi trong dấu ngoặc):
ansi1252.txt
(50): Mã hóa châu Âu 1252 (nó sẽ được hiển thị đầy đủ mà không bị méo mó trong Windows với ngôn ngữ châu Âu)unicode1.txt
(102): Unicode hai byte, ở đầu là BOM của Windows0xFFFE
unicode2.txt
(100): Unicode hai byte không có BOM (nói chung, BOM là tùy chọn)unicode3.txt
(102): Unicode hai byte, ở đầu có BOM đặc trưng của Unix,0xFEFF
utf8.txt
(54): Mã hóa UTF-8
Trong hàm OnStart
, chúng ta sẽ đọc các tệp này trong vòng lặp với các cài đặt khác nhau của FileOpen
. Lưu ý rằng bằng cách sử dụng FileHandle
(đã được xem xét trong phần trước), chúng ta không phải lo lắng về việc đóng tệp: mọi thứ diễn ra tự động trong mỗi lần lặp.
void OnStart()
{
Print("=====> UTF-8");
for(int i = 0; i < ArraySize(texts); ++i)
{
FileHandle fh(FileOpen(texts[i], FILE_READ | FILE_TXT | FILE_ANSI, 0, CP_UTF8));
Print(texts[i], " -> ", FileReadString(~fh));
}
Print("=====> Unicode");
for(int i = 0; i < ArraySize(texts); ++i)
{
FileHandle fh(FileOpen(texts[i], FILE_READ | FILE_TXT | FILE_UNICODE));
Print(texts[i], " -> ", FileReadString(~fh));
}
Print("=====> ANSI/1252");
for(int i = 0; i < ArraySize(texts); ++i)
{
FileHandle fh(FileOpen(texts[i], FILE_READ | FILE_TXT | FILE_ANSI, 0, 1252));
Print(texts[i], " -> ", FileReadString(~fh));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Hàm FileReadString
đọc một chuỗi từ tệp. Chúng ta sẽ đề cập đến nó trong phần về ghi và đọc biến.
Dưới đây là một ví dụ nhật ký với kết quả thực thi script:
=====> UTF-8
MQL5Book/ansi1252.txt -> This is a text with special characters: ?? / ? / ?
MQL5Book/unicode1.txt -> This is a text with special characters: ±Σ / £ / ¥
MQL5Book/unicode2.txt -> T
MQL5Book/unicode3.txt -> ??
MQL5Book/utf8.txt -> This is a text with special characters: ±Σ / £ / ¥
=====> Unicode
MQL5Book/ansi1252.txt -> 桔獩椠整瑸眠瑩灳捥慩档牡捡整獲㾱⼠ꌠ⼠ꔠ
MQL5Book/unicode1.txt -> This is a text with special characters: ±Σ / £ / ¥
MQL5Book/unicode2.txt -> This is a text with special characters: ±Σ / £ / ¥
MQL5Book/unicode3.txt -> 吀栀椀猀 椀猀 愀 琀攀砀琀 眀椀琀栀 猀瀀攀挀椀愀氀 挀栀愀爀愀挀琀攀爀猀㨀 넀
MQL5Book/utf8.txt -> 桔獩椠整瑸眠瑩灳捥慩档牡捡整獲뇂ꏎ⼠술₣ ꗂ
=====> ANSI/1252
MQL5Book/ansi1252.txt -> This is a text with special characters: ±? / £ / ¥
MQL5Book/unicode1.txt -> This is a text with special characters: ±Σ / £ / ¥
MQL5Book/unicode2.txt -> T
MQL5Book/unicode3.txt -> þÿ
MQL5Book/utf8.txt -> This is a text with special characters: ±Σ / £ / ¥
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Tệp unicode1.txt
luôn được đọc chính xác vì nó có BOM 0xFFFE
, và hệ thống bỏ qua các cài đặt trong mã nguồn. Tuy nhiên, nếu nhãn này bị thiếu hoặc là big-endian, việc tự động phát hiện này không hoạt động. Ngoài ra, khi đặt FILE_UNICODE
, chúng ta mất khả năng đọc các văn bản một byte và UTF-8.
Kết quả là, sự kết hợp đã đề cập của FILE_ANSI
và CP_UTF8
nên được xem là kháng cự hơn đối với các biến thể trong định dạng. Việc chọn một trang mã quốc gia cụ thể chỉ được khuyến nghị khi được yêu cầu rõ ràng.
Mặc dù API cung cấp sự hỗ trợ đáng kể cho lập trình viên khi làm việc với các tệp ở chế độ văn bản, chúng ta có thể, nếu cần, tránh chế độ FILE_TXT
hoặc FILE_CSV
, và mở một tệp văn bản ở chế độ nhị phân FILE_BINARY
. Điều này sẽ chuyển toàn bộ độ phức tạp của việc phân tích văn bản và xác định mã hóa sang vai trò của lập trình viên, nhưng nó sẽ cho phép hỗ trợ các định dạng không chuẩn khác. Nhưng điểm chính ở đây là văn bản có thể được đọc từ và ghi vào một tệp được mở ở chế độ nhị phân. Tuy nhiên, điều ngược lại, trong trường hợp chung, là không thể. Một tệp nhị phân với dữ liệu tùy ý (tức là không chỉ chứa chuỗi) được mở ở chế độ văn bản rất có thể sẽ được diễn giải thành văn bản "vô nghĩa". Nếu bạn cần ghi dữ liệu nhị phân vào một tệp văn bản, trước tiên hãy sử dụng hàm CryptEncode
và mã hóa CRYPT_BASE64
.