Working with files. Part III

February 28, 2010
By Bogdan Baltatu, MQLmagazine editor

[Versiunea romaneasca] [MQLmagazine.com in romana] [English edition]

This is the third article of the ‘Working with files’ series and I care to announce you that this is one of the most important articles of the series because I’ll present the writing functions as well as examples that will show some aspects of the functions.

The functions that refer strictly file writing are 8. These functions are:

1
2
FileWrite() , FileWriteArray() , FileWriteDouble() , FileWriteFloat() ,
FileWriteInteger() , FileWriteLong() , FileWriteString() , FileWriteStruct()

In the continuation I’ll present each function with its notes.

FileWrite()

The function is used for writing data in CSV files. The prototype of the function is:

1
2
3
4
uint  FileWrite(
   int  file_handle    // file handle
   ...                 // List of parameters to be written
   );

The file_handle parameter represents the result of the FileOpen() function, that has to be called with the flags FILE_CSV|FILE_WRITE. If the file is not open with the FILE_WRITE flag, then FileWrite() will fail.

The parameter list to be written in the CSV file cannot be larger than 63, parameters will be separated by the delimiter specified when we opened the file with the FileOpen() function. Should we have not specify it at that time, the ‘tab’ delimiter would have been default (ASCII 9).
The parameter type is not specified, they could be integer, datetime , float , … . It is important to know that the FileWrite() function converts the parameters in strings. The double parameters have 16 digits after the decimal dot, the float parameters have 5, and the datetime parameters are converted to the ‘YYYY.MM.DD HH:MI:SS’.
Each FileWrite() command puts at the end of the generated string ‘rn’. This shows us that after writing the string the cursor moves to the next row.

1
2
int file_handle=FileOpen("text.csv",FILE_ANSI|FILE_CSV|FILE_WRITE);
uint chars_written=FileWrite(file_handle,1,3,4,5,6,7);

In Excel 2007, even if delimiter is ‘tab’ and the parameters should be displayed on a separate column they don’t show up. If we rename the file to ‘.txt’ and we open it with Excel 2007 then parameters will be each on a column because it admits the ‘tab’ character. The issue is not with Excel and it deserves some study. It is recommended to open the CSV with an editor as notepad or notepad++ if it has more than 65535 lines.

FileWriteArray()

The function writes elements of different types of array, except for the string ones, in files opened as FILE_BIN.

The prototype of the function is:

1
2
3
4
5
6
int  FileWriteArray(
int   file_handle                   
void  array[],                     
int   start_item=0,             
int   items_count=WHOLE_ARRAY   
);

The parameters of the function are file_handle , the result of the FileOpen(); array , the array that is source for the elements to be written in the file; start_item , position in the array where to start writing from; items_count , the number of elements to be copied starting from start_item.

The result of the FileWriteArray() function is the number of elements written in the file, so the result of the function has to be equal to items_count for the function call to be considered successfully.

If the table has 9 elements and we send an items_count larger than 9, the FileWriteArray() will not return any error, because it will write all the 9 elements.

Below you have a code example which writes a matrix in a binary file (opened with FILE_BIN):

1
2
3
double array1[3][3]={{1.0, 2.0, 3.0}, {4.0, 5.0, 6.0}, {7.0, 8.0, 9.0}};
int file_handle=FileOpen("text.bin",FILE_BIN|FILE_WRITE);
int elements_written=FileWriteArray(file_handle,array1,0,WHOLE_ARRAY);

String arrays have to be written in TXT files. Each element, when written in the file, is followed by the ‘rn’ characters, so each table element is written on a new line. Below we exemplified in a code sample the functionality of FileWriteArray() if the matrix is string.

1
2
3
string array1[3][3]={{"a", "b", "c"}, {"d", "e", "f"}, {"g", "h", "i"}};
int file_handle=FileOpen("text.txt",FILE_TXT|FILE_WRITE|FILE_ANSI);
int elements_written=FileWriteArray(file_handle,array1,6,3);

FileWriteDouble()

The function writes in a file from the current cursor position a value of the double type. The function’s prototype is:

1
2
3
4
uint  FileWriteDouble(
   int     file_handle      
   double  dvalue           
   );

Parameters are two: file_handle is the file handle returned by FileOpen() and dvalue is the double value that we wish to have it written in the file.

The function is considered successful if the result is 8, meaningly the length in bytes of a double type variable, and the pointer moves by 8 bytes.

Below is a code example.

1
2
fh=FileOpen("test.bin",FILE_WRITE|FILE_BIN);
   s=FileWriteDouble(fh,a);

Warning: To use the FileWriteDouble() function, the file has to be opened binary, otherwise the function fails.

FileWriteFloat()

The FileWriteFloat() function resembles FileWriteDouble() , only that the variable to be written is of float type. The function’s prototype is:

1
2
3
4
uint  FileWriteFloat(
int    file_handle      
float  fvalue           
);

The function parameters are file_handle , the reference of the file given by FileOpen() and the float value that we wish to have it written in our file.

Code example.

1
2
fh=FileOpen("test.bin",FILE_WRITE|FILE_BIN);
   s=FileWriteFloat(fh,a);

Of course, the result has to be 4, the length in bytes of a float type variable, for a correct writing.

FileWriteInteger()

The function writes an integer typed variable in a binary file beginning from the current pointer. The function’s prototype is:

1
2
3
4
5
uint  FileWriteInteger(
int  file_handle         
int  ivalue,              
int  size=INT_VALUE 
);

The parameters of the function are : the file handle, the value that we wish to have written, and its length in bytes.
The last parameter may be 1, 2 or 4 (1=char , 2=short, 4=integer).
The function returns 1,2 or 4 accordingly to the size parameter and the returned value has to be equal with this, if the execution was correct.

Code example:

1
2
3
int a=2;
   fh=FileOpen("test.bin",FILE_WRITE|FILE_BIN);
   s=FileWriteInteger(fh,a,4);

The file has to be opened as binary, otherwise the function fails. If the function is correctly executed then pointer moves with as much bytes as the function returns (1, 2 or 4).

We have to specify that by ‘int’ in the function’s prototype the compiler actually understands any integer type, and the function does not automatically presume signed integers, being able to work with unsigned integers.

FileWriteLong()

The function writes a long typed value ion a binary file beginning with the current cursor position in the file. The function’s prototype is:

1
2
3
4
uint  FileWriteLong(
int   file_handle      
long  lvalue           
);

The parameters of the function are : the file handle, returned by the FileOpen() call and the value that we want to have written. If the function executes successfully, it returns 8, the length in bytes of a long typed variable.

Code example

1
2
3
long a=212313123;
fh=FileOpen("test.bin",FILE_WRITE|FILE_BIN);
s=FileWriteLong(fh,a);

FileWriteString()

The function writes a string in text or binary file beginning with the current position of the cursor. The function’s prototype is:

1
2
3
4
5
uint  FileWriteString(
   int     file_handle      
   string  svalue,         
   int     size=-1          
   );

The parameters of the function are : the file handle, the string that we want to have written, and the number of characters contained by the string.
The third parameter is mandatory if the file has been opened with FILE_BIN, but optional when the file has been opened as FILE_TXT.

If the execution is successful then the result is the number of bytes written in the file and the cursor’s position moves by this number of bytes.

It should be noted that when we use the function to write in a FILE_UNICODE file (specified or not, because FILE_UNICODE is default), the number of bytes written is twice the number of characters. When we write with FILE_ANSI flag, the number of bytes is the same as the number of characters. MetaQuotes’ decision is a bit awkward , to make FILE_UNICODE default instead of FILE_ANSI. It should be normal that FILE_ANSI is default,
De precizat ar fi ca atunci cand folosim functia pentru a scrie intr-un fisier deschis cu FILE_UNICODE (specificat sau nu, caci FILE_UNICODE e default) numarul de bytes scrisi este de 2 ori mai mare decat numarul de caractere. Cand scriem cu flagul FILE_ANSI numarul de bytes scrisi coincide cu numarul de caractere. E cel putin ciudata decizia MetaQuotes sa faca FILE_UNICODE default in loc de FILE_ANSI. Normal ar fi ca FILE_ANSI sa fie default, to avoid the useless doubling of the text files, being known that the majority of users will open them with Notepad or Excel.

Code example.

1
2
3
 string a="A1b2C3";
fh=FileOpen("test.txt",FILE_WRITE|FILE_ANSI);
s=FileWriteString(fh,a);

FileWriteStruct()

The function writes in a binary typed file the contents of a structure parsed as parameter beginning with the current cursor position. The prototype of the function is:

1
2
3
4
5
uint  FileWriteStruct(
int                file_handle     
any_simple_struct  str_object&,   
int                size             
);

The function parameters are : the file handle returned by FileOpen(), the structure (by reference) and the length in bytes that has to be written. If the function is executed successfully it returns the number of written bytes.

Code example:

1
2
3
4
5
 MqlRates a[1];  
CopyRates(Symbol(),PERIOD_D1,0,1,a);
int so=sizeof(a);
fh=FileOpen("te.bin",FILE_WRITE|FILE_BIN);   
s=FileWriteStruct(fh,a,so);

When we define the structure ‘a’, we have to define it as array, to be able to use it with the CopyRates() function, and it has to have at least one element.

It should be known that the files opened with FILE_BIN where we wrote something will contain weird characters if opened. They are not wrongly written. Even if the files are binary we saved them as ‘.txt’ to be easily to be opened with the text editor for you to see easier that something was written.

It seems that MetaQuotes has chosen a complicated path for files. I’ll make a short review on how Borland Pascal knew files, so you can see how simple was at that time (the ’90s).
First, Borland Pascal didn’t have the concept of file handle, a number to identify the file. It had only file typed variables. These were complex variables, known only by compiler – they couldn’t have been read, written or interogated. The functions that dealt with reading/writing were about 6 : ReadLn, WriteLn, Read, Write, BlockRead, BlockWrite. ReadLn and WriteLn were working only with the screen, keyboard and text files, while Read and Write were also working on the “binary” level. Due to the special nature of the file variables, Read and Write knew from the beginning if they have an I/O operation with keyboard/screen or with a file. There was no need of FileRead or FileWrite, because if the first parameter was a file typed variable, it couldn’t have been mistaken with an integer or anything else – the functions knew from beginning what they work with. If it was file of text , and you were writing Write(f,128) , the function would have written “128”, text, on 3 characters, and if the file would have been file of byte, the function would have written a byte with the 128 value.
Also, the strict definition of variables made the difference between types and functions knew from beginning how many bytes they had to read/write for any data type. If file had a complex type , like file of record (record was the equivalent for struct from MQL5), then functions were reading or writing one by one many structures in a row, incrementing the cursor with the number of structures, not with the number of bytes. BlockRead and BlockWrite were working with any kind of variables, being interested only in the length of the read/written blocks (they were working with untyped files).

Be careful when using file functions. Otherwise, you can lose time recording data that are saved in a wrong manner!