forked from rosh/MQL5Book
171 lines
6.4 KiB
MQL5
171 lines
6.4 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| FileSaveLoad.mq5 |
| |
//| Copyright 2021, MetaQuotes Ltd. |
| |
//| https://www.mql5.com |
| |
//+------------------------------------------------------------------+
| |
| |
#define PRT(A) Print(#A, "=", (A))
| |
| |
//+------------------------------------------------------------------+
| |
//| Struct for compound field in Simple struct |
| |
//+------------------------------------------------------------------+
| |
struct Pair
| |
{
| |
short x, y;
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Example struct: structs with simple fields only are allowed for |
| |
//| functions FileSave/FileLoad; this can't be a class |
| |
//+------------------------------------------------------------------+
| |
struct Simple
| |
{
| |
double d;
| |
int i;
| |
datetime t;
| |
color c;
| |
uchar a[10]; // fixed size array is ok
| |
bool b;
| |
Pair p; // compound fields (nested simple structs) are allowed as well
| |
| |
// string field or dynamic array will cause the following compile error:
| |
// structures or classes containing objects are not allowed
| |
// string s;
| |
// uchar a[];
| |
| |
// pointers are also prohibited for FileSave/FileLoad
| |
// void *ptr;
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Custom equivalent of FileSave function |
| |
//+------------------------------------------------------------------+
| |
template<typename T>
| |
bool MyFileSave(const string name, const T &array[], const int flags = 0)
| |
{
| |
const int h = FileOpen(name, FILE_BIN | FILE_WRITE | flags);
| |
if(h == INVALID_HANDLE) return false;
| |
FileWriteArray(h, array);
| |
FileClose(h);
| |
return true;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Custom equivalent of FileLoad function |
| |
//+------------------------------------------------------------------+
| |
template<typename T>
| |
long MyFileLoad(const string name, T &array[], const int flags = 0)
| |
{
| |
const int h = FileOpen(name, FILE_BIN | FILE_READ | flags);
| |
if(h == INVALID_HANDLE) return -1;
| |
const uint n = FileReadArray(h, array, 0, (int)(FileSize(h) / sizeof(T)));
| |
// the next check is our custom improvement over standard FileLoad:
| |
// we show the warning if file size is not a multiple of struct size
| |
const ulong leftover = FileSize(h) - FileTell(h);
| |
if(leftover != 0)
| |
{
| |
PrintFormat("Warning from %s: Some data left unread: %d bytes",
| |
__FUNCTION__, leftover);
| |
SetUserError((ushort)leftover);
| |
}
| |
FileClose(h);
| |
return n;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Output byte array in hex |
| |
//+------------------------------------------------------------------+
| |
void ByteArrayPrint(const uchar &bytes[],
| |
const int row = 16, const string separator = " | ",
| |
const uint start = 0, const uint count = WHOLE_ARRAY)
| |
{
| |
string hex = "";
| |
const int n = (int)MathCeil(MathLog10(ArraySize(bytes) + 1));
| |
for(uint i = start; i < MathMin(start + count, ArraySize(bytes)); ++i)
| |
{
| |
if(i % row == 0 || i == start)
| |
{
| |
if(hex != "") Print(hex);
| |
hex = StringFormat("[%0*d]", n, i) + " ";
| |
}
| |
hex += StringFormat("%02X", bytes[i]) + separator;
| |
}
| |
if(hex != "") Print(hex);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Script program start function |
| |
//+------------------------------------------------------------------+
| |
void OnStart()
| |
{
| |
Simple write[] =
| |
{
| |
{+1.0, -1, D'2021.01.01', clrBlue, {'a'}, true, {1000, 16000}},
| |
{-1.0, -2, D'2021.01.01', clrRed, {'b'}, true, {1000, 16000}},
| |
};
| |
const string filename = "MQL5Book/rawdata";
| |
| |
// PART I. Normal execution
| |
| |
PRT(FileSave(filename, write/*, FILE_COMMON*/)); // true
| |
// PRT(MyFileSave(filename, simple/*, FILE_COMMON*/)); // does the same
| |
| |
Simple read[];
| |
PRT(FileLoad(filename, read/*, FILE_COMMON*/)); // 2
| |
// PRT(MyFileLoad(filename, read/*, FILE_COMMON*/)); // does the same
| |
| |
// make sure that the read data is equal to what has been written
| |
PRT(ArrayCompare(write, read)); // 0
| |
| |
// convert struct array to byte array to show its raw presentation
| |
uchar bytes[];
| |
for(int i = 0; i < ArraySize(read); ++i)
| |
{
| |
uchar temp[];
| |
PRT(StructToCharArray(read[i], temp));
| |
ArrayCopy(bytes, temp, ArraySize(bytes));
| |
}
| |
ByteArrayPrint(bytes);
| |
/*
| |
Output:
| |
| |
[00] 00 | 00 | 00 | 00 | 00 | 00 | F0 | 3F | FF | FF | FF | FF | 00 | 66 | EE | 5F |
| |
[16] 00 | 00 | 00 | 00 | 00 | 00 | FF | 00 | 61 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
| |
[32] 00 | 00 | 01 | E8 | 03 | 80 | 3E | 00 | 00 | 00 | 00 | 00 | 00 | F0 | BF | FE |
| |
[48] FF | FF | FF | 00 | 66 | EE | 5F | 00 | 00 | 00 | 00 | FF | 00 | 00 | 00 | 62 |
| |
[64] 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 01 | E8 | 03 | 80 | 3E |
| |
*/
| |
| |
uchar bytes2[];
| |
// we can read directly in byte array
| |
PRT(FileLoad(filename, bytes2/*, FILE_COMMON*/)); // 78, 39 * 2
| |
PRT(ArrayCompare(bytes, bytes2)); // 0, equality
| |
| |
// PART II. Various problematic stuff
| |
| |
// example of error-free yet incorrect usage:
| |
// the receiving struct type does not correspond to actual stored data
| |
MqlDateTime mdt[];
| |
PRT(sizeof(MqlDateTime)); // 32
| |
// Warning from MyFileLoad<MqlDateTime>: Some data left unread: 14 bytes
| |
PRT(MyFileLoad(filename, mdt)); // 2
| |
ArrayPrint(mdt);
| |
/*
| |
Output is a mess:
| |
| |
[year] [mon] [day] [hour] [min] [sec] [day_of_week] [day_of_year]
| |
[0] 0 1072693248 -1 1609459200 0 16711680 97 0
| |
[1] -402587648 4096003 0 -20975616 16777215 6286950 -16777216 1644167168
| |
*/
| |
| |
/*
| |
// compile error, because type string is not supported here
| |
string texts[];
| |
FileSave("any", texts); // parameter conversion not allowed
| |
*/
| |
| |
double data[];
| |
PRT(FileLoad("any", data)); // -1, no such file
| |
PRT(_LastError); // 5004, ERR_CANNOT_OPEN_FILE
| |
}
| |
//+------------------------------------------------------------------+
|