Ghi và đọc biến (tệp văn bản)
Tệp văn bản có một tập hợp hàm riêng để lưu trữ nguyên tử (từng phần tử) và đọc dữ liệu. Nó hơi khác so với tập hợp dành cho tệp nhị phân trong phần trước. Cũng cần lưu ý rằng không có các hàm tương đương để ghi/đọc một cấu trúc hoặc một mảng cấu trúc vào tệp văn bản. Nếu bạn cố gắng sử dụng bất kỳ hàm nào trong số này với tệp văn bản, chúng sẽ không có hiệu quả mà sẽ gây ra mã lỗi nội bộ 5011 (FILE_NOTBIN
).
Như chúng ta đã biết, tệp văn bản trong MQL5 có hai dạng: văn bản thuần túy và văn bản ở định dạng CSV. Chế độ tương ứng, FILE_TXT
hoặc FILE_CSV
, được đặt khi tệp được mở và không thể thay đổi nếu không đóng và lấy lại mô tả. Sự khác biệt giữa chúng chỉ xuất hiện khi đọc tệp. Cả hai chế độ đều được ghi lại theo cùng một cách.
Ở chế độ TXT, mỗi lần gọi hàm đọc (bất kỳ hàm nào chúng ta sẽ xem xét trong phần này) sẽ tìm dòng mới tiếp theo trong tệp (ký tự \n
hoặc cặp \r\n
) và xử lý mọi thứ cho đến đó. Mục đích của việc xử lý là chuyển đổi văn bản từ tệp thành một giá trị của kiểu cụ thể tương ứng với hàm được gọi. Trong trường hợp đơn giản nhất, nếu hàm FileReadString
được gọi, không có xử lý nào được thực hiện (chuỗi được trả về "nguyên trạng").
Ở chế độ CSV, mỗi lần gọi hàm đọc, văn bản trong tệp được phân tách logic không chỉ bởi các dòng mới mà còn bởi một ký tự phân cách bổ sung được chỉ định khi mở tệp. Phần còn lại của việc xử lý đoạn từ vị trí hiện tại của tệp đến ký tự phân cách gần nhất là tương tự.
Nói cách khác, việc đọc văn bản và di chuyển vị trí nội bộ trong tệp được thực hiện theo từng đoạn từ ký tự phân cách này đến ký tự phân cách khác, trong đó ký tự phân cách không chỉ có nghĩa là ký tự delimiter
trong danh sách tham số của FileOpen
mà còn là dòng mới (\n
, \r\n
), cũng như đầu và cuối tệp.
Ký tự phân cách bổ sung có cùng tác động khi ghi văn bản vào tệp FILE_TXT
và FILE_CSV
, nhưng chỉ khi sử dụng hàm FileWrite
: nó tự động chèn ký tự này giữa các phần tử được ghi. Dấu phân cách của hàm FileWriteString
bị bỏ qua.
Hãy xem xét mô tả chính thức của các hàm, sau đó xem xét một ví dụ trong FileTxtCsv.mq5
.
uint FileWrite(int handle, ...)
Hàm này thuộc danh mục các hàm nhận số lượng tham số biến đổi. Các tham số như vậy được biểu thị trong nguyên mẫu hàm bằng dấu ba chấm. Chỉ các kiểu dữ liệu tích hợp được hỗ trợ. Để ghi cấu trúc hoặc đối tượng lớp, bạn phải giải tham chiếu các phần tử của chúng và truyền từng phần tử riêng lẻ.
Hàm ghi tất cả các đối số được truyền sau đối số đầu tiên vào tệp văn bản với mô tả handle
. Các đối số được phân tách bằng dấu phẩy, như trong danh sách đối số thông thường. Số lượng đối số xuất ra tệp không thể vượt quá 63.
Khi xuất ra, dữ liệu số được chuyển đổi sang định dạng văn bản theo quy tắc chuyển đổi chuẩn sang (string)
. Giá trị hoặc kiểu double
được xuất ra với 16 chữ số có nghĩa, ở định dạng truyền thống hoặc định dạng số mũ khoa học (tùy chọn nhỏ gọn hơn được chọn). Dữ liệu kiểu float
được hiển thị với độ chính xác 7 chữ số có nghĩa. Để hiển thị số thực với độ chính xác khác hoặc ở định dạng được chỉ định rõ ràng, hãy sử dụng hàm DoubleToString
(xem Số sang chuỗi và ngược lại).
Giá trị kiểu datetime
được xuất ra ở định dạng "YYYY.MM.DD hh:mm:ss" (xem Ngày và giờ).
Màu chuẩn (từ danh sách màu web) được hiển thị dưới dạng tên, màu không chuẩn được hiển thị dưới dạng bộ ba giá trị thành phần RGB (xem Màu sắc), phân tách bằng dấu phẩy (lưu ý: dấu phẩy là ký tự phân cách phổ biến nhất trong CSV).
Đối với các liệt kê, một số nguyên biểu thị phần tử được hiển thị thay vì định danh (tên) của nó. Ví dụ, khi ghi FRIDAY (từ ENUM_DAY_OF_WEEK
, xem Liệt kê), chúng ta nhận được số 5 trong tệp.
Giá trị kiểu bool
được xuất ra dưới dạng chuỗi "true" hoặc "false".
Nếu một ký tự phân cách khác 0 được chỉ định khi mở tệp, nó sẽ được chèn giữa hai dòng liền kề kết quả từ việc chuyển đổi các đối số tương ứng.
Khi tất cả các đối số được ghi vào tệp, một ký tự kết thúc dòng \r\n
được thêm vào.
Hàm trả về số byte đã ghi, hoặc 0 trong trường hợp lỗi.
uint FileWriteString(int handle, const string text, int length = -1)
Hàm này ghi tham số chuỗi text
vào tệp văn bản với mô tả handle
. Tham số length
chỉ áp dụng cho tệp nhị phân và bị bỏ qua trong ngữ cảnh này (dòng được ghi đầy đủ).
Hàm
FileWriteString
cũng có thể hoạt động với tệp nhị phân. Ứng dụng này của hàm được mô tả trong phần trước.
Bất kỳ dấu phân cách nào (giữa các phần tử trong một dòng) và các dòng mới phải được chèn/thêm vào bởi lập trình viên.
Hàm trả về số byte đã ghi (trong chế độ FILE_UNICODE
, điều này sẽ gấp đôi độ dài chuỗi tính bằng ký tự) hoặc 0 trong trường hợp lỗi.
string FileReadString(int handle, int length = -1)
Hàm này đọc một chuỗi cho đến ký tự phân cách tiếp theo từ tệp với mô tả handle
(ký tự phân cách trong tệp CSV, ký tự xuống dòng trong bất kỳ tệp nào, hoặc cho đến cuối tệp). Tham số length
chỉ áp dụng cho tệp nhị phân và bị bỏ qua trong ngữ cảnh này.
Chuỗi kết quả có thể được chuyển đổi thành giá trị của kiểu cần thiết bằng cách sử dụng quy tắc giảm chuẩn hoặc sử dụng hàm chuyển đổi. Ngoài ra, có thể sử dụng các hàm đọc chuyên dụng: FileReadBool
, FileReadDatetime
, FileReadNumber
được mô tả dưới đây.
Trong trường hợp lỗi, một chuỗi rỗng sẽ được trả về. Mã lỗi có thể được tìm thấy thông qua biến _LastError
hoặc hàm GetLastError. Đặc biệt, khi đến cuối tệp, mã lỗi sẽ là 5027 (FILE_ENDOFFILE
).
bool FileReadBool(int handle)
Hàm này đọc một đoạn của tệp CSV cho đến ký tự phân cách tiếp theo, hoặc cho đến cuối dòng và chuyển đổi nó thành giá trị kiểu bool
. Nếu đoạn chứa văn bản "true" (ở bất kỳ trường hợp nào, bao gồm cả trường hợp hỗn hợp, ví dụ "True"), hoặc một số khác không, chúng ta nhận được true
. Trong các trường hợp khác, chúng ta nhận được false
.
Từ "true" phải chiếm toàn bộ phần tử được đọc. Ngay cả khi chuỗi bắt đầu bằng "true", nhưng có phần tiếp theo (ví dụ, "True Volume"), chúng ta nhận được false
.
datetime FileReadDatetime(int handle)
Hàm này đọc từ tệp CSV một chuỗi ở một trong các định dạng sau: "YYYY.MM.DD hh:mm:ss", "YYYY.MM.DD" hoặc "hh:mm:ss", và chuyển đổi nó thành giá trị kiểu datetime
. Nếu đoạn không chứa biểu diễn văn bản hợp lệ của ngày và/hoặc giờ, hàm sẽ trả về số không hoặc thời gian "kỳ lạ", tùy thuộc vào những ký tự nào nó có thể diễn giải là các đoạn ngày và giờ. Đối với chuỗi rỗng hoặc không phải số, chúng ta nhận được ngày hiện tại với thời gian bằng không.
Việc đọc ngày và giờ linh hoạt hơn (hỗ trợ nhiều định dạng hơn) có thể đạt được bằng cách kết hợp hai hàm: StringToTime(FileReadString(handle))
. Để biết thêm chi tiết về StringToTime
, xem Ngày và giờ.
double FileReadNumber(int handle)
Hàm này đọc một đoạn từ tệp CSV cho đến ký tự phân cách tiếp theo hoặc cho đến cuối dòng, và chuyển đổi nó thành giá trị kiểu double
theo quy tắc ép kiểu chuẩn.
Lưu ý rằng double
có thể mất độ chính xác của các giá trị rất lớn, điều này có thể ảnh hưởng đến việc đọc các số lớn kiểu long/ulong
(giá trị mà sau đó các số nguyên trong double
bị méo mó là 9007199254740992: ví dụ về hiện tượng này được đưa ra trong phần Unions).
Các hàm được thảo luận trong phần trước, bao gồm
FileReadDouble
,FileReadFloat
,FileReadInteger
,FileReadLong
vàFileReadStruct
, không thể áp dụng cho tệp văn bản.
Tập lệnh FileTxtCsv.mq5
thể hiện cách làm việc với tệp văn bản. Lần trước chúng ta đã tải báo giá lên tệp nhị phân. Bây giờ hãy thực hiện điều đó ở định dạng TXT và CSV.
Về cơ bản, MetaTrader 5 cho phép xuất và nhập báo giá ở định dạng CSV từ hộp thoại "Symbols". Nhưng vì mục đích giáo dục, chúng ta sẽ tái hiện quá trình này. Ngoài ra, việc triển khai phần mềm cho phép bạn thay đổi so với định dạng chính xác được tạo mặc định. Một đoạn lịch sử XAUUSD H1 được xuất theo cách chuẩn được hiển thị dưới đây.
<DATE> » <TIME> » <OPEN> » <HIGH> » <LOW> » <CLOSE> » <TICKVOL> » <VOL> » <SPREAD>
2021.01.04 » 01:00:00 » 1909.07 » 1914.93 » 1907.72 » 1913.10 » 4230 » 0 » 5
2021.01.04 » 02:00:00 » 1913.04 » 1913.64 » 1909.90 » 1913.41 » 2694 » 0 » 5
2021.01.04 » 03:00:00 » 1913.41 » 1918.71 » 1912.16 » 1916.61 » 6520 » 0 » 5
2021.01.04 » 04:00:00 » 1916.60 » 1921.89 » 1915.49 » 1921.79 » 3944 » 0 » 5
2021.01.04 » 05:00:00 » 1921.79 » 1925.26 » 1920.82 » 1923.19 » 3293 » 0 » 5
2021.01.04 » 06:00:00 » 1923.20 » 1923.71 » 1920.24 » 1922.67 » 2146 » 0 » 5
2021.01.04 » 07:00:00 » 1922.66 » 1922.99 » 1918.93 » 1921.66 » 3141 » 0 » 5
2021.01.04 » 08:00:00 » 1921.66 » 1925.60 » 1921.47 » 1922.99 » 3752 » 0 » 5
2021.01.04 » 09:00:00 » 1922.99 » 1925.54 » 1922.47 » 1924.80 » 2895 » 0 » 5
2021.01.04 » 10:00:00 » 1924.85 » 1935.16 » 1924.59 » 1932.07 » 6132 » 0 » 5
2
3
4
5
6
7
8
9
10
11
Ở đây, đặc biệt, chúng ta có thể không hài lòng với ký tự phân cách mặc định (tab, được biểu thị là '"'), thứ tự của các cột, hoặc việc ngày và giờ bị chia thành hai trường.
Trong tập lệnh của chúng ta, chúng ta sẽ chọn dấu phẩy làm ký tự phân cách, và chúng ta sẽ tạo các cột theo thứ tự của các trường của cấu trúc MqlRates
. Việc tải xuống và đọc thử sau đó sẽ được thực hiện ở chế độ FILE_TXT
và FILE_CSV
.
cpp const string txtfile = "MQL5Book/atomic.txt"; const string csvfile = "MQL5Book/atomic.csv"; const short delimiter = ',';
Báo giá sẽ được yêu cầu ở đầu hàm OnStart
theo cách chuẩn:
void OnStart()
{
MqlRates rates[];
int n = PRTF(CopyRates(_Symbol, _Period, 0, 10, rates)); // 10
2
3
4
Chúng ta sẽ chỉ định tên của các cột trong mảng riêng biệt, và cũng kết hợp chúng bằng hàm trợ giúp StringCombine
. Các tiêu đề riêng biệt là cần thiết vì chúng ta kết hợp chúng thành một tiêu đề chung bằng ký tự phân cách có thể chọn (một giải pháp thay thế có thể dựa trên StringReplace
). Chúng ta khuyến khích bạn làm việc với mã nguồn của StringCombine
một cách độc lập: nó thực hiện thao tác ngược lại so với hàm tích hợp StringSplit.
const string columns[] = {"DateTime", "Open", "High", "Low", "Close",
"Ticks", "Spread", "True"};
const string caption = StringCombine(columns, delimiter) + "\r\n";
2
3
Cột cuối cùng lẽ ra nên được gọi là "Volume", nhưng chúng ta sẽ sử dụng ví dụ của nó để kiểm tra hiệu suất của hàm FileReadBool
. Bạn có thể giả định rằng tên hiện tại ngụ ý "True Volume" (nhưng chuỗi như vậy sẽ không được diễn giải là true
).
Tiếp theo, hãy mở hai tệp ở chế độ FILE_TXT
và FILE_CSV
, và ghi tiêu đề đã chuẩn bị vào chúng.
int fh1 = PRTF(FileOpen(txtfile, FILE_TXT | FILE_ANSI | FILE_WRITE, delimiter));//1
int fh2 = PRTF(FileOpen(csvfile, FILE_CSV | FILE_ANSI | FILE_WRITE, delimiter));//2
PRTF(FileWriteString(fh1, caption)); // 48
PRTF(FileWriteString(fh2, caption)); // 48
2
3
4
5
Vì hàm FileWriteString
không tự động thêm dòng mới, chúng ta đã thêm "\r\n" vào biến caption
.
for(int i = 0; i < n; ++i)
{
FileWrite(fh1, rates[i].time,
rates[i].open, rates[i].high, rates[i].low, rates[i].close,
rates[i].tick_volume, rates[i].spread, rates[i].real_volume);
FileWrite(fh2, rates[i].time,
rates[i].open, rates[i].high, rates[i].low, rates[i].close,
rates[i].tick_volume, rates[i].spread, rates[i].real_volume);
}
FileClose(fh1);
FileClose(fh2);
2
3
4
5
6
7
8
9
10
11
12
Việc ghi các trường cấu trúc từ mảng rates
được thực hiện theo cùng một cách, bằng cách gọi FileWrite
trong một vòng lặp cho mỗi tệp trong hai tệp. Hãy nhớ rằng hàm FileWrite
tự động chèn ký tự phân cách giữa các đối số và thêm "\r\n" ở cuối chuỗi. Tất nhiên, có thể tự mình chuyển đổi tất cả các giá trị đầu ra thành chuỗi và gửi chúng vào tệp bằng FileWriteString
, nhưng sau đó chúng ta sẽ phải tự xử lý các dấu phân cách và dòng mới. Trong một số trường hợp, chúng không cần thiết, ví dụ, nếu bạn đang ghi ở định dạng JSON ở dạng nhỏ gọn (về cơ bản trong một dòng khổng lồ).
Do đó, ở giai đoạn ghi, cả hai tệp đều được quản lý theo cùng một cách và hóa ra là giống nhau. Dưới đây là ví dụ về nội dung của chúng cho XAUUSD,H1 (kết quả của bạn có thể khác):
DateTime,Open,High,Low,Close,Ticks,Spread,True
2021.08.19 12:00:00,1785.3,1789.76,1784.75,1789.06,4831,5,0
2021.08.19 13:00:00,1789.06,1790.02,1787.61,1789.06,3393,5,0
2021.08.19 14:00:00,1789.08,1789.95,1786.78,1786.89,3536,5,0
2021.08.19 15:00:00,1786.78,1789.86,1783.73,1788.82,6840,5,0
2021.08.19 16:00:00,1788.82,1792.44,1782.04,1784.02,9514,5,0
2021.08.19 17:00:00,1784.04,1784.27,1777.14,1780.57,8526,5,0
2021.08.19 18:00:00,1780.55,1784.02,1780.05,1783.07,5271,6,0
2021.08.19 19:00:00,1783.06,1783.15,1780.73,1782.59,3571,7,0
2021.08.19 20:00:00,1782.61,1782.96,1780.16,1780.78,3236,10,0
2021.08.19 21:00:00,1780.79,1780.9,1778.54,1778.65,1017,13,0
2
3
4
5
6
7
8
9
10
11
Sự khác biệt trong việc làm việc với các tệp này sẽ bắt đầu xuất hiện ở giai đoạn đọc.
Hãy mở một tệp văn bản để đọc và "quét" nó bằng hàm FileReadString
trong một vòng lặp, cho đến khi nó trả về một chuỗi rỗng (tức là cho đến cuối tệp).
string read;
fh1 = PRTF(FileOpen(txtfile, FILE_TXT | FILE_ANSI | FILE_READ, delimiter)); // 1
Print("===== Reading TXT");
do
{
read = PRTF(FileReadString(fh1));
}
while(StringLen(read) > 0);
2
3
4
5
6
7
8
Nhật ký sẽ hiển thị một cái gì đó như thế này:
===== Reading TXT
FileReadString(fh1)=DateTime,Open,High,Low,Close,Ticks,Spread,True / ok
FileReadString(fh1)=2021.08.19 12:00:00,1785.3,1789.76,1784.75,1789.06,4831,5,0 / ok
FileReadString(fh1)=2021.08.19 13:00:00,1789.06,1790.02,1787.61,1789.06,3393,5,0 / ok
FileReadString(fh1)=2021.08.19 14:00:00,1789.08,1789.95,1786.78,1786.89,3536,5,0 / ok
FileReadString(fh1)=2021.08.19 15:00:00,1786.78,1789.86,1783.73,1788.82,6840,5,0 / ok
FileReadString(fh1)=2021.08.19 16:00:00,1788.82,1792.44,1782.04,1784.02,9514,5,0 / ok
FileReadString(fh1)=2021.08.19 17:00:00,1784.04,1784.27,1777.14,1780.57,8526,5,0 / ok
FileReadString(fh1)=2021.08.19 18:00:00,1780.55,1784.02,1780.05,1783.07,5271,6,0 / ok
FileReadString(fh1)=2021.08.19 19:00:00,1783.06,1783.15,1780.73,1782.59,3571,7,0 / ok
FileReadString(fh1)=2021.08.19 20:00:00,1782.61,1782.96,1780.16,1780.78,3236,10,0 / ok
FileReadString(fh1)=2021.08.19 21:00:00,1780.79,1780.9,1778.54,1778.65,1017,13,0 / ok
FileReadString(fh1)= / FILE_ENDOFFILE(5027)
2
3
4
5
6
7
8
9
10
11
12
13
Mọi cuộc gọi của FileReadString
đọc toàn bộ dòng (cho đến \r\n
) ở chế độ FILE_TXT
. Để tách nó thành các phần tử, chúng ta nên triển khai xử lý bổ sung. Tùy chọn, chúng ta có thể sử dụng chế độ FILE_CSV
.
Hãy thực hiện tương tự cho tệp CSV.
fh2 = PRTF(FileOpen(csvfile, FILE_CSV | FILE_ANSI | FILE_READ, delimiter)); // 2
Print("===== Reading CSV");
do
{
read = PRTF(FileReadString(fh2));
}
while(StringLen(read) > 0);
2
3
4
5
6
7
Lần này sẽ có nhiều mục nhập hơn trong nhật ký:
===== Reading CSV
FileReadString(fh2)=DateTime / ok
FileReadString(fh2)=Open / ok
FileReadString(fh2)=High / ok
FileReadString(fh2)=Low / ok
FileReadString(fh2)=Close / ok
FileReadString(fh2)=Ticks / ok
FileReadString(fh2)=Spread / ok
FileReadString(fh2)=True / ok
FileReadString(fh2)=2021.08.19 12:00:00 / ok
FileReadString(fh2)=1785.3 / ok
FileReadString(fh2)=1789.76 / ok
FileReadString(fh2)=1784.75 / ok
FileReadString(fh2)=1789.06 / ok
FileReadString(fh2)=4831 / ok
FileReadString(fh2)=5 / ok
FileReadString(fh2)=0 / ok
...
FileReadString(fh2)=2021.08.19 21:00:00 / ok
FileReadString(fh2)=1780.79 / ok
FileReadString(fh2)=1780.9 / ok
FileReadString(fh2)=1778.54 / ok
FileReadString(fh2)=1778.65 / ok
FileReadString(fh2)=1017 / ok
FileReadString(fh2)=13 / ok
FileReadString(fh2)=0 / ok
FileReadString(fh2)= / FILE_ENDOFFILE(5027)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Ý nghĩa là hàm FileReadString
trong chế độ FILE_CSV
tính đến ký tự phân cách và chia các chuỗi thành các phần tử. Mọi cuộc gọi FileReadString
trả về một giá trị duy nhất (ô) từ bảng CSV. Rõ ràng, các chuỗi kết quả cần được chuyển đổi sau đó thành các kiểu thích hợp.
Vấn đề này có thể được giải quyết ở dạng tổng quát bằng cách sử dụng các hàm chuyên dụng FileReadDatetime
, FileReadNumber
, FileReadBool
. Tuy nhiên, trong bất kỳ trường hợp nào, nhà phát triển phải theo dõi số của cột hiện đang đọc và xác định ý nghĩa thực tế của nó. Một ví dụ về thuật toán như vậy được đưa ra trong bước thứ ba của bài kiểm tra. Nó sử dụng cùng tệp CSV (để đơn giản, chúng ta đóng nó ở cuối mỗi bước và mở lại ở bước tiếp theo).
Để đơn giản hóa việc gán trường tiếp theo trong cấu trúc MqlRates
theo số cột, chúng ta đã tạo một cấu trúc con MqlRates
chứa một phương thức mẫu set
:
struct MqlRatesM : public MqlRates
{
template<typename T>
void set(int field, T v)
{
switch(field)
{
case 0: this.time = (datetime)v; break;
case 1: this.open = (double)v; break;
case 2: this.high = (double)v; break;
case 3: this.low = (double)v; break;
case 4: this.close = (double)v; break;
case 5: this.tick_volume = (long)v; break;
case 6: this.spread = (int)v; break;
case 7: this.real_volume = (long)v; break;
}
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Trong hàm OnStart
, chúng ta đã mô tả một mảng của một cấu trúc như vậy, nơi chúng ta sẽ thêm các giá trị đến. Mảng được yêu cầu để đơn giản hóa việc ghi nhật ký với ArrayPrint
(không có hàm sẵn trong MQL5 để in một cấu trúc riêng lẻ).
Print("===== Reading CSV (alternative)");
MqlRatesM r[1];
int count = 0;
int column = 0;
const int maxColumn = ArraySize(columns);
2
3
4
5
Biến count
đếm các bản ghi không chỉ cần thiết cho thống kê mà còn là phương tiện để bỏ qua dòng đầu tiên, chứa tiêu đề而不是 dữ liệu. Số cột hiện tại được theo dõi trong biến column
. Giá trị tối đa của nó không nên vượt quá số cột maxColumn
.
Bây giờ chúng ta chỉ cần mở tệp và đọc các phần tử từ nó trong một vòng lặp bằng các hàm khác nhau cho đến khi xảy ra lỗi, đặc biệt là lỗi dự kiến như 5027 (FILE_ENDOFFILE
), tức là khi đến cuối tệp.
Khi số cột là 0, chúng ta áp dụng hàm FileReadDatetime
. Đối với các cột khác, sử dụng FileReadNumber
. Ngoại lệ là trường hợp của dòng đầu tiên với tiêu đề: đối với điều này, chúng ta gọi hàm FileReadBool
để thể hiện cách nó sẽ phản ứng với tiêu đề "True" được thêm cố ý vào cột cuối cùng.
fh2 = PRTF(FileOpen(csvfile, FILE_CSV | FILE_ANSI | FILE_READ, delimiter)); // 1
do
{
if(column)
{
if(count == 1) // demo cho FileReadBool trên bản ghi đầu tiên với tiêu đề
{
r[0].set(column, PRTF(FileReadBool(fh2)));
}
else
{
r[0].set(column, FileReadNumber(fh2));
}
}
else // cột 0 là ngày và giờ
{
++count;
if(count > 1) // cấu trúc từ dòng trước đã sẵn sàng
{
ArrayPrint(r, _Digits, NULL, 0, 1, 0);
}
r[0].time = FileReadDatetime(fh2);
}
column = (column + 1) % maxColumn;
}
while(_LastError == 0); // thoát khi đến cuối tệp 5027 (FILE_ENDOFFILE)
// in cấu trúc cuối cùng
if(column == maxColumn - 1)
{
ArrayPrint(r, _Digits, NULL, 0, 1, 0);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Đây là những gì được ghi lại:
===== Reading CSV (alternative)
FileOpen(csvfile,FILE_CSV|FILE_ANSI|FILE_READ,delimiter)=1 / ok
FileReadBool(fh2)=false / ok
FileReadBool(fh2)=false / ok
FileReadBool(fh2)=false / ok
FileReadBool(fh2)=false / ok
FileReadBool(fh2)=false / ok
FileReadBool(fh2)=false / ok
FileReadBool(fh2)=true / ok
2021.08.19 00:00:00 0.00 0.00 0.00 0.00 0 0 1
2021.08.19 12:00:00 1785.30 1789.76 1784.75 1789.06 4831 5 0
2021.08.19 13:00:00 1789.06 1790.02 1787.61 1789.06 3393 5 0
2021.08.19 14:00:00 1789.08 1789.95 1786.78 1786.89 3536 5 0
2021.08.19 15:00:00 1786.78 1789.86 1783.73 1788.82 6840 5 0
2021.08.19 16:00:00 1788.82 1792.44 1782.04 1784.02 9514 5 0
2021.08.19 17:00:00 1784.04 1784.27 1777.14 1780.57 8526 5 0
2021.08.19 18:00:00 1780.55 1784.02 1780.05 1783.07 5271 6 0
2021.08.19 19:00:00 1783.06 1783.15 1780.73 1782.59 3571 7 0
2021.08.19 20:00:00 1782.61 1782.96 1780.16 1780.78 3236 10 0
2021.08.19 21:00:00 1780.79 1780.90 1778.54 1778.65 1017 13 0
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Như bạn thấy, trong tất cả các tiêu đề, chỉ có tiêu đề cuối cùng được chuyển đổi thành giá trị true
, và tất cả các tiêu đề trước đó là false
.
Nội dung của các cấu trúc được đọc giống với dữ liệu ban đầu.