Implementierung von praktischen Modulen aus anderen Sprachen in MQL5 (Teil 06): Python-ähnliche Datei-IO-Operationen in MQL5
Inhalt
- Einführung
- Verstehen der Funktion für E/A-Operationen in Python
- Automatisches Auswählen von Datei-Flags
- Python-ähnliche Open-Methode in MQL5
- Lesen von Daten/Informationen aus Dateien
- Schreiben von Daten/Informationen in die Dateien
- Zusätzliche Methoden
- Schlussfolgerung
Einführung
Dateioperationen sind für jede Programmiersprache unerlässlich. Sie helfen unseren Programmen, über den Code mit externen Dateien zu interagieren, und unterstützen uns beim Import und Export von Informationen. Bei Hunderten, wenn nicht Tausenden Dateitypen, die in moderner Software zur Verfügung stehen, benötigen wir bessere und effektivere Möglichkeiten, Informationen in und aus diesen Dateien zu verarbeiten (zu lesen und zu schreiben).

Die Programmiersprache MQL5 verfügt über verschiedene integrierte Möglichkeiten zum Lesen und Schreiben von unzähligen Dateitypen, die jedoch nicht immer ausreichen.
Anders als in MQL5, wo die Dateioperationen expliziter und Flag-gesteuert sind, kann dies dazu führen, dass einfache und regelmäßige Aufgaben wie das Lesen von CSV-Dateien kompliziert und fehleranfällig werden. In der Programmiersprache Python sind die Dateieingabe und -ausgabe dank einer umfangreichen Standardbibliothek, die viele Details auf niedriger Ebene, mit denen MQL5-Entwickler konfrontiert sind, abstrahiert, einfach und äußerst flexibel. Siehe das folgende Beispiel zum Lesen derselben TEXT-Datei sowohl in MQL5 als auch in Python:
In MQL5:
void OnStart() { //--- string filename = "readme.txt"; int handle = FileOpen(filename,FILE_READ|FILE_TXT|FILE_ANSI, "", CP_UTF8); if (handle == INVALID_HANDLE) { printf("Failed to open '%s' Error = %d",filename,GetLastError()); return; } while (!FileIsEnding(handle)) { string data = FileReadString(handle); Print(data); } }
In Python:
with open(f"{files_path}\\readme.txt", "r") as file: for line in file: print(line.rstrip())
Das Lesen der gleichen Datei in Python war mühelos und viel effektiver, da die Nutzer im Gegensatz zu MQL5 die Kontrolle über die aus der Datei gewonnenen Zeilen hatten.
In diesem Artikel werden wir untersuchen, wie Dateieingabe/-ausgabe in MQL5 im Vergleich zu Python funktioniert und wie wir übergeordnete (Python-ähnliche) Abstraktionen auf der nativen API entwickeln können. Ziel ist es, einen einfachen, aber effektiven und sicheren Ansatz für E/A-Operationen in der Programmiersprache MQL5 zu bieten.
Verstehen der Funktion für E/A-Operationen in Python
Um eine Funktion für E/A-Operationen in MQL5 wie in Python zu erstellen, müssen wir die innere Funktionsweise einer Funktion namens open verstehen.
Die integrierte Funktion open() in Python wird verwendet, um eine Datei zu öffnen und ein entsprechendes Dateiobjekt zurückzugeben. Mit dieser Funktion können Sie aus Dateien lesen oder in Dateien schreiben, wobei verschiedene Optionen für den Dateimodus (z. B. Text/Binär) und die Kodierung zur Verfügung stehen.
Funktionssignatur.
open( file, mode="r", buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None )
Argumente.
| Argument | Beschreibung | Standardwert |
|---|---|---|
| Datei | Ein pfadähnliches Objekt, das den Pfadnamen der zu öffnenden Datei angibt. | Erforderlich. |
| Modus | Eine Zeichenkette zur Angabe des Modus, in dem die Datei geöffnet werden soll (z. B. 'r', 'w', 'b' usw.). | 'r'. |
| Pufferung | Eine ganze Zahl, die zur Festlegung der Pufferungsrichtlinie verwendet wird. | -1 |
| Kodierung | Der Name der Kodierungsmethode, die zur Kodierung oder Dekodierung der Datei verwendet wird. | Nein |
| Zeilenumbruch | Eine Zeichenkette, die festlegt, wie neue Zeichen aus dem Stream geparst werden sollen. | Nein |
| closefd | Ein boolescher Wert, der festlegt, ob ein Dateideskriptor geschlossen werden soll. | true |
| Öffner | Ein aufrufbares Programm, mit dem nutzerdefiniert die Zieldatei geöffnet werden soll. | Nein |
In unserer äquivalenten MQL5-Funktion gibt es eine Reihe von Variablen, die nützlich sein könnten.
int CFileIO::open(const string filename, const string mode, uint cp_encoding = CP_UTF8, const bool common = false, const string newline = "", bool is_unicode=false);
Weitere Variablen wie „common“ (zur Auswahl, ob sich die Datei im Verzeichnis „common“ oder im MQL5-Datenpfad befindet) und die Variable „is_unicode“ (zur Auswahl, ob die Datei Unicode-Zeichen enthält – bei „true“ handelt es sich um Zeichenfolgen vom Typ Unicode (Zweibyt-Zeichen), bei „false“ um Zeichenfolgen vom Typ ANSI (Ein-Byte-Zeichen)).
Das interessanteste Argument der Funktion open ist mode.
Der Dateimodus teilt Python mit, welche Art von Operationen (Lesen, Schreiben usw.) Sie mit der Datei durchführen wollen.
| Modus | Beschreibung |
|---|---|
| 'r' | Schreibgeschützt. Gibt einen E/A-Fehler aus, wenn die Datei nicht existiert. |
| 'r+' | Lesen und Schreiben. Gibt einen E/A-Fehler aus, wenn die Datei nicht existiert. |
| 'w' | Nur Schreibzugriff. Überschreibt die Datei, wenn sie vorhanden ist; andernfalls wird eine neue Datei erstellt. |
| 'w+' | Lesen und Schreiben. Überschreibt die Datei oder erstellt eine neue Datei. |
| 'a' | Nur anhängen. Fügt Daten am Ende hinzu. Erzeugt eine Datei, wenn sie nicht existiert. |
| 'a+' | Lesen und Anhängen. Zeiger wird am Ende platziert. Erzeugt eine Datei, wenn sie nicht existiert. |
| 'rb' | Lesen im Binärmodus. Es muss eine Datei vorhanden sein. |
| 'rb+' | Lesen und Schreiben im Binärmodus. Die Datei muss existieren. |
| 'wb' | Schreiben in binärer Form. Überschreibt oder erstellt neu. |
| 'wb+' | Lesen und Schreiben im Binärformat. Überschreibt oder erstellt neu. |
| 'ab' | Anhängen in binärer Form. Erzeugt eine Datei, wenn sie nicht existiert. |
| 'ab+' | Lesen und Anhängen im Binärformat. Erzeugt eine Datei, wenn sie nicht existiert. |
Damit sich unsere MQL5-Funktion wie ihr Python-Pendant verhält, wenn es darum geht, eine beliebige Datei zu öffnen, unabhängig davon, was sie benötigt, brauchen wir eine Funktion, die uns hilft, die Flags automatisch zu erzeugen, je nach Modus einer Datei.
Automatisches Auswählen von Datei-Flags
Da die in MQL5 integrierte Funktion FileOpen in hohem Maße auf die sogenannten Datei-Flags angewiesen ist, benötigen wir eine Möglichkeit, diese automatisch entsprechend dem/den oben beschriebenen Dateimodus(en) zu erzeugen.
int CFileIO::flagsgen(const string file_mode, bool &is_append) { //--- default flag(s) for txt files int flags = 0; string mode = file_mode; StringToLower(mode); for(int i = 0; i < (int)mode.Length(); i++) { switch(StringGetCharacter(mode, i)) { case 'r': flags |= (FILE_READ | FILE_SHARE_READ); break; case 'w': flags |= (FILE_WRITE | FILE_SHARE_WRITE); break; case 'a': { flags |= FILE_WRITE; is_append = true; break; } case '+': flags |= FILE_READ | FILE_WRITE | FILE_SHARE_READ | FILE_SHARE_WRITE; break; case 'b': flags |= FILE_BIN; break; case 'x': flags |= (FILE_REWRITE | FILE_WRITE | FILE_SHARE_WRITE); break; } } return flags; }
Die Variable is_append ist nützlich für den Aufruf der Methode FileSeek beim Anhängen der Informationen am Ende einer Datei.
Beachten Sie, dass wir FILE_SHARE_READ haben, wenn es ein FILE_READ-Flag gibt, und FILE_SHARE_WRITE, wenn es ein FILE_WRITE-Flag gibt.
Damit soll der Prozess des Lesens und Schreibens in eine Datei, die von anderen Programmen verwendet wird, verstärkt werden.
Um dies noch besser zu machen, können wir eine optionale Variable shared_IO verwenden (wenn sie auf true gesetzt ist, bedeutet dies, dass wir I/O-Operationen auf Dateien durchführen können, die von anderen Programmen verwendet werden, und andere Programme können dasselbe tun, wenn eine Datei in MetaTrader 5 geöffnet wird).
int CFileIO::flagsgen(const string file_mode, bool &is_append, bool shared_IO=true) { //--- default flag(s) for txt files int flags = 0; string mode = file_mode; StringToLower(mode); for(int i = 0; i < (int)mode.Length(); i++) { switch(StringGetCharacter(mode, i)) { case 'r': flags |= FILE_READ; if (shared_IO) flags |= FILE_SHARE_READ; break; case 'w': flags |= FILE_WRITE; if (shared_IO) flags |= FILE_SHARE_WRITE; break; case 'a': { flags |= FILE_WRITE; is_append = true; break; } case '+': flags |= FILE_READ | FILE_WRITE; if (shared_IO) flags |= FILE_SHARE_READ | FILE_SHARE_WRITE; break; case 'b': flags |= FILE_BIN; break; case 'x': flags |= FILE_REWRITE | FILE_WRITE; if (shared_IO) flags |= FILE_SHARE_WRITE; break; } } return flags; }
Der Wert wird direkt von der Funktion namens „open“ übergeben.
static int open(const string filename, const string mode, uint cp_encoding = CP_UTF8, const bool common = false, const string newline = "", bool is_unicode=false, bool shared_IO=true);
Python-ähnliche offene Methode in MQL5
Mithilfe der Flags, die je nach Modus einer Datei erzeugt werden, können wir nun jede beliebige Datei öffnen.
int CFileIO::open(const string filename, const string mode, uint cp_encoding = CP_UTF8, const bool common = false, const string newline = "", bool is_unicode=false, bool shared_IO=true) { //--- bool is_append = false; int flags = flagsgen(mode, is_append, shared_IO); string file_extension = getFileExtension(filename); //--- if (file_extension=="") return INVALID_HANDLE; //--- we add select a file from the common folder if commo=true if(common) flags |= FILE_COMMON; //--- bool is_binary = (flags & FILE_BIN) != 0; if (!is_binary) //Avoid unicode and ANSI flags during a binary mode { if (is_unicode) flags |= FILE_UNICODE; else flags |= FILE_ANSI; } //--- Open a file for either reading or writing int h = FileOpen(filename, flags, newline, cp_encoding); if(h == INVALID_HANDLE) { printf("Failed to read '%s', Error = %s", filename, fileErrorsDescription(GetLastError())); return INVALID_HANDLE; } //--- if(is_append) FileSeek(h, 0, SEEK_END); return h; }
Das Erzeugen von Flags in Abhängigkeit vom Dateimodus reicht nicht aus; wir müssen einige sehr nützliche Flags an die primären Flags anhängen. Diese Flags helfen bei:
01: der Identifikation, wo sich die Datei befindet (entweder im MQL5-Datenpfad oder im Ordner „common“)
if(common) flags |= FILE_COMMON;
02: der Entscheidung, ob ANSI oder UNICODE zum Lesen von Bytesymbolen verwendet werden soll.
bool is_binary = (flags & FILE_BIN) != 0; if (!is_binary) //Avoid unicode and ANSI flags during a binary mode { if (is_unicode) flags |= FILE_UNICODE; else flags |= FILE_ANSI; }
Es scheint, dass MQL5 eine Datei als Bytestream behandelt, wenn ein Binärflag FILE_BIN vorhanden ist; man muss sich nicht viel um UNICODE- und ANSI-Flags kümmern.
Jetzt können wir diese universelle Funktion verwenden, um verschiedene Arten von Dateien in MetaTrader 5 zu öffnen.
#include <PyMQL5\\fileIO\\fileIO.mqh> //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- CFileIO::open("readme.txt", "r+"); //open the file in read/write mode CFileIO::open("MT5.log", "r"); //readonly CFileIO::open("mydata.csv", "r+"); //read/write mode for a CSV file CFileIO::open("mydata.xlsx", "r"); //A less common filetype CFileIO::open("tiny-cat.jpg", "rb"); //an image file, readonly binary file mode CFileIO::open("array.bin", "w+b"); //Read and write mode for a binary file }
Alle Dateien wurden erfolgreich in MetaTrader 5 geöffnet, da im Terminal keine Fehler angezeigt wurden, wie es bei einem Fehlschlagen der Funktion zu erwarten wäre.
Die Funktion open gibt ein Handle zu einer geöffneten Datei zurück. Sie können die Datei immer noch mit den nativen MQL5-Funktionen für die Dateibearbeitung bearbeiten, einschließlich des Schließens der Datei, wenn Sie sie nicht mehr benötigen.
Die Rückgabe eines Handles bedeutet jedoch, dass wir es immer noch manuell verwalten müssen; es wäre ideal, wenn es eine Klasse zurückgeben würde, die alle Eigenschaften und Methoden einer bestimmten Datei enthält.
Die Klasse CFile (Objekt)
class CFile { protected: int m_handle; string m_filename; int m_flags; bool isHandleOk(string func) { if(m_handle == INVALID_HANDLE) { printf("%s Invalid file handle received", func); return false; } return true; } public: CFile(void) { m_handle = INVALID_HANDLE; m_flags = 0; m_filename = ""; }; ~CFile(void) { }; //--- configurations void Config(const string filename, const int handle, const int flags) { m_filename = filename; m_handle = handle; m_flags = flags; } void close(); };
Dies sollte uns nun eine problemlose Handhabung und Manipulation der geöffneten Datei ermöglichen.
void OnStart() { //--- CFile f = CFileIO::open("readme.txt", "r"); //open the file in read-only mode f.close(); //closing after you are done with it f = CFileIO::open("MT5.log", "r"); //readonly f.close(); f = CFileIO::open("mydata.csv", "r+"); //read/write mode for a CSV file f.close(); f = CFileIO::open("array.bin", "wb+"); f.close(); }
Lesen von Daten/Informationen aus Dateien
Das Tolle an Dateioperationen in Python ist, dass sie dem Nutzer die Kontrolle über das Lesen und Interpretieren der von den Dateien erhaltenen Informationen geben.
import csv files_path = r"C:\Users\omega\AppData\Roaming\MetaQuotes\Terminal\FB9A56D617EDDDFE29EE54EBEFFE96C1\MQL5\Files" with open(f"{files_path}\\readme.txt", "r") as file: for line in file: # reading a file line by line print(line.rstrip()) with open(f"mydata.csv", "r", encoding="utf-8-sig", newline='') as csvfile: csvreader = csv.reader(csvfile, delimiter=',') for row in csvreader: # reading a csv file row by row print(row)
Ausgabe:
hello, this is a readme file with plenty of information to read from. This is a third line after a space. ['DateTime', 'Open', 'High', 'Low', 'Close'] ['12/27/2023 19:00', '2081.72', '2082.53', '2079.52', '2081.94'] ['12/27/2023 18:00', '2078.97', '2082.41', '2076.73', '2081.69'] ['12/27/2023 17:00', '2070.29', '2081.88', '2069.01', '2078.93'] ['12/27/2023 16:00', '2068.33', '2071.62', '2066.6', '2070.3']
MQL5 bietet uns zwar auch eine Möglichkeit, die Informationen über die Zeilen einer Datei in einer while-Schleife zu verfolgen, die durch alle Zeilen einer Datei geht, aber der Code in Python fühlt sich viel besser an. Lassen Sie uns eine ähnliche Funktionalität in MQL5 implementieren.
Da MQL5 mehrere Funktionen zum Lesen von Daten aus Dateien hat, wie z.B. FileReadString, FileReadDouble, FileReadLong, usw., können wir eine Vorlage verwenden, um die Funktion mit allen unterstützten Datentypen arbeiten zu lassen, sodass sich der Nutzer über den Typ der Variablen, die er per Referenz übergibt, Gedanken machen muss, da er den resultierenden Datentyp auf der Grundlage des Variablentyps erhält.
template <typename T> T CFile::__readline__() { T datatype = T(0); // string if(typename(T) == typename(string)) datatype = (T)FileReadString(m_handle); // int if(typename(T) == typename(int)) datatype = (T)FileReadInteger(m_handle); // long if(typename(T) == typename(long)) datatype = (T)FileReadLong(m_handle); // double if(typename(T) == typename(double)) datatype = (T)FileReadDouble(m_handle); // float (read as double and cast) if(typename(T) == typename(float)) datatype = (T)FileReadDouble(m_handle); // bool (read as int and cast) if(typename(T) == typename(bool)) datatype = (T)FileReadInteger(m_handle); // datetime (read as long and cast) if(typename(T) == typename(datetime)) datatype = (T)FileReadLong(m_handle); return datatype; }
Diese Funktion kann dann innerhalb einer öffentlichen Funktion namens „readline“ vererbt werden.
template <typename T> bool CFile::readline(T &line) { if(!isHandleOk(__FUNCTION__)) return false; //--- while(!FileIsEnding(m_handle)) { line = __readline__<T>(); return true; } return false; }
Beispiel für die Verwendung:
#include <PyMQL5\\fileIO\\fileIO.mqh> #include <PyMQL5\\fileIO\\csv.mqh> //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- Reading a text file CFile f = CFileIO::open("readme.txt", "r"); //open the file in read-only mode string text; while(f.readline(text)) Print(text); f.close(); //closing after you are done with it }
Ausgabe:
ND 0 22:34:35.591 Test file IO (EURUSD,H1) hello, this is a readme file with a plenty of information to read from. DD 0 22:34:35.591 Test file IO (EURUSD,H1) GG 0 22:34:35.591 Test file IO (EURUSD,H1) This is a third line after a space.
Diese Funktion ist sogar in der Lage, Binärdateien zu lesen.
void OnStart() { f = CFileIO::open("array.bin", "wb+"); int value, count = 0; while (f.readline(value)) { printf("array[%d]: %d",count,value); count++; } f.close(); }
Ausgabe:
PI 0 17:27:44.966 Test file IO (EURUSD,H1) array[0]: 1 RR 0 17:27:44.966 Test file IO (EURUSD,H1) array[1]: 2 PK 0 17:27:44.966 Test file IO (EURUSD,H1) array[2]: 3 ND 0 17:27:44.966 Test file IO (EURUSD,H1) array[3]: 4 PM 0 17:27:44.966 Test file IO (EURUSD,H1) array[4]: 5 RF 0 17:27:44.966 Test file IO (EURUSD,H1) array[5]: 6
Diese Funktion, readline, funktioniert für verschiedene Dateitypen wie ein Zauber. Bei der Arbeit mit CSV-Dateien benötigen wir einige spezielle Funktionen zum Parsen der Zeilen und zum sicheren Extrahieren des Inhalts aus allen Zeilen.
In Python gibt es ein kleines Modul namens csv, das für das Lesen und Schreiben von bzw. in CSV-Dateien zuständig ist.
import csv with open(f"mydata.csv", "r", encoding="utf-8-sig", newline='') as csvfile: csvreader = csv.reader(csvfile, delimiter=',') for row in csvreader: # reading a csv file row by row print(row)
Ausgabe:
['DateTime', 'Open', 'High', 'Low', 'Close'] ['12/27/2023 19:00', '2081.72', '2082.53', '2079.52', '2081.94'] ['12/27/2023 18:00', '2078.97', '2082.41', '2076.73', '2081.69'] ['12/27/2023 17:00', '2070.29', '2081.88', '2069.01', '2078.93'] ['12/27/2023 16:00', '2068.33', '2071.62', '2066.6', '2070.3'] ['12/27/2023 15:00', '2067.68', '2069.73', '2066.15', '2068.38']
Haben Sie bemerkt, dass Python alle Werte aus jeder Zeile einer CSV-Datei als Zeichenketten liest?
Das ist großartig, denn von allen Variablen lassen sich Zeichenketten am sichersten in andere Variablen umwandeln, ganz zu schweigen davon, dass CSV-Dateien in der Regel verschiedene Datentypen enthalten. Es ist eine gute Idee, sie alle zusammen in einem Array vom Typ string zu speichern.
Wir können eine ähnliche Klasse in MQL5 erstellen.
#include "fileIO.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CSVReader { protected: int m_handle; string m_delimiter; char m_quote; bool m_doublequote; bool m_skipinitialspace; char m_escape; uint cols_found; string StringTrim(string s) { StringTrimLeft(s); StringTrimRight(s); return s; } void ParseCSVLine(string line, string &fields[]); public: CSVReader(CFile &file, const string delimiter = ",", const char quotechar = '"', const char escapechar = '\\', const bool doublequote = true, const bool skipinitialspace = false ); ~CSVReader(void); bool readRow(string &row[]); };
Um zu verhindern, dass große Dateien geöffnet werden, können wir einige Kontrollen einbauen.
Wir prüfen, ob eine Datei eine bestimmte Größe überschreitet.
#define MAX_FILE_SIZE_MB 200
//--- Getting the file size in MegaBytes double file_size_MB = (double)FileSize(m_handle) / (double)1e6; printf("%s Filesize in ~ MB [%.3f]", __FUNCTION__, file_size_MB); if((uint)file_size_MB > MAX_FILE_SIZE_MB) { printf("%s Failed, CSV filesize [%.3f] in MBs is greater than the maximum file size accepted [%I64u] in MBs. To pass this limit, change the variable 'MAX_FILE_SIZE_MB'", __FUNCTION__, file_size_MB, MAX_FILE_SIZE_MB); return; }
Wir prüfen auch, ob genügend Speicherplatz für die zu öffnende Datei vorhanden ist.
//--- Ensuring the CSV file size doesn't exceed available memory for the Terminal ulong free_ram_MB = (ulong)TerminalInfoInteger(TERMINAL_MEMORY_AVAILABLE); printf("Free Terminal RAM ~ %I64u MB", free_ram_MB); //--- The CSV file isn't supposed to be greater in size than half of the available memory if(file_size_MB >= free_ram_MB) { printf("Filesize in MB [%.3f] is greater than available memory [%I64u] in the Terminal", file_size_MB, free_ram_MB); return; }
Alle diese Prüfungen finden in einem Klassenkonstruktor statt.
CSVReader::CSVReader(CFile &file, const string delimiter = ",", const char quotechar = '"', const char escapechar = '\\', const bool doublequote = true, const bool skipinitialspace = false) { //--- m_handle = file.getHandle(); m_delimiter = delimiter; m_quote = quotechar; m_doublequote = doublequote; m_skipinitialspace = skipinitialspace; m_escape = escapechar; //--- Getting the file size in MegaBytes double file_size_MB = (double)FileSize(m_handle) / (double)1e6; printf("%s Filesize in ~ MB [%.3f]", __FUNCTION__, file_size_MB); if((uint)file_size_MB > MAX_FILE_SIZE_MB) { printf("%s Failed, CSV filesize [%.3f] in MBs is greater than the maximum file size accepted [%I64u] in MBs. To pass this limit, change the variable 'MAX_FILE_SIZE_MB'", __FUNCTION__, file_size_MB, MAX_FILE_SIZE_MB); return; } //--- Ensuring the CSV file size doesn't exceed available memory for the Terminal ulong free_ram_MB = (ulong)TerminalInfoInteger(TERMINAL_MEMORY_AVAILABLE); printf("Free Terminal RAM ~ %I64u MB", free_ram_MB); //--- The CSV file isn't supposed to be greater than half of the available memory if(file_size_MB >= free_ram_MB) { printf("Filesize in MB [%.3f] is greater than available memory [%I64u] in the Terminal", file_size_MB, free_ram_MB); return; } }
Konstruktor-Argumente
| Argument | Beschreibung | Standard |
|---|---|---|
| m_handle | Ein gültiges CSV-Datei-Handle, das von FileOpen() zurückgegeben wird. Sie bezieht sich auf eine bereits geöffnete CSV-Datei. Der Leser arbeitet direkt mit diesem Handle und verwaltet nicht das Öffnen oder Schließen der Datei. | Erforderlich |
| m_delimiter | Eine Zeichenkette, die zur Trennung von Feldern innerhalb einer Zeile verwendet wird. Übliche Werte sind "," (Komma), ";" (Semikolon) und "\t" (Tabulator). | "," |
| m_quotechar | Das Zeichen, das für Anführungszeichen in Feldern verwendet wird, die Begrenzungszeichen oder Sonderzeichen enthalten. Alles, was sich innerhalb passender Anführungszeichen befindet, wird als literale Daten behandelt. | ' " ' |
| m_escapechar | Das Zeichen, das verwendet wird, um Sonderzeichen innerhalb eines Feldes mit Anführungszeichen zu umgehen. Mit \" kann zum Beispiel ein Anführungszeichen innerhalb eines in Anführungszeichen gesetzten Wertes erscheinen. | '\\' |
| m_doublequote | Das steuert, wie Anführungszeichen innerhalb von Anführungszeichenfeldern behandelt werden. Wenn true, werden zwei aufeinanderfolgende Anführungszeichen ("") als ein einzelnes wörtliches Anführungszeichen interpretiert, was dem Standardverhalten von CSV entspricht. | "" |
| m_skipinitialspace | Wenn diese Option aktiviert ist, werden Leerzeichen unmittelbar nach dem Begrenzungszeichen ignoriert. Dies ist nützlich für das Parsen von lose formatierten CSV-Dateien wie „A,B,C“ anstelle von „A, B, C“. | false |
Nachdem wir eine CSV-Datei geöffnet haben, erstellen wir ein CSVReader-Objekt und weisen es einer Variablen namens „reader“ zu. Dann erstellen wir ein Array namens row[], und alle Zeilen aus einer CSV-Datei werden iterativ in diesem Array gespeichert.
void OnStart() { int csv_file = CFileIO::open("mydata.csv", "r+"); //read/write mode for a CSV file CSVReader reader(csv_file, ","); string row[]; while(reader.readRow(row)) ArrayPrint(row); CFileIO::close(csv_file); }
Ausgabe:
CP 0 00:51:24.810 Test file IO (EURUSD,H1) CSVReader::CSVReader Filesize in ~ MB [0.001] CD 0 00:51:24.815 Test file IO (EURUSD,H1) Free Terminal RAM ~ 32245 MB IJ 0 00:51:24.816 Test file IO (EURUSD,H1) "ÿDateTime" "Open" "High" "Low" "Close" "Strings Column" HS 0 00:51:24.816 Test file IO (EURUSD,H1) [0] "12/27/2023 19:00" "2081.72" "2082.53" GJ 0 00:51:24.816 Test file IO (EURUSD,H1) [3] "2079.52" "2081.94" "Yes, this column has text with commas." DM 0 00:51:24.816 Test file IO (EURUSD,H1) "12/27/2023 18:00" "2078.97" "2082.41" "2076.73" "2081.69" "None" DQ 0 00:51:24.816 Test file IO (EURUSD,H1) "12/27/2023 17:00" "2070.29" "2081.88" "2069.01" "2078.93" "None" DH 0 00:51:24.816 Test file IO (EURUSD,H1) "12/27/2023 16:00" "2068.33" "2071.62" "2066.6" "2070.3" "Some value" PK 0 00:51:24.816 Test file IO (EURUSD,H1) [0] "12/27/2023 15:00" "2067.68" "2069.73" NE 0 00:51:24.816 Test file IO (EURUSD,H1) [3] "2066.15" "2068.38" "Another value, with comma" PI 0 00:51:24.816 Test file IO (EURUSD,H1) "12/27/2023 14:00" "2068.21" "2070.29" "2064.37" "2067.69" "None" CM 0 00:51:24.816 Test file IO (EURUSD,H1) "12/27/2023 13:00" "2064.73" "2068.87" "2064.62" "2068.19" "None" EL 0 00:51:24.816 Test file IO (EURUSD,H1) "12/27/2023 12:00" "2068.38" "2068.72" "2061.51" "2064.75" "Some value" HE 0 00:51:24.816 Test file IO (EURUSD,H1) "12/27/2023 11:00" "2067.39" "2069.28" "2067.31" "2068.38" "None" DH 0 00:51:24.816 Test file IO (EURUSD,H1) "12/27/2023 10:00" "2066.09" "2068.31" "2065.85" "2067.38" "None" CM 0 00:51:24.816 Test file IO (EURUSD,H1) "12/27/2023 9:00" "2065.06" "2066.38" "2064.81" "2066.09" "None" KO 0 00:51:24.816 Test file IO (EURUSD,H1) "12/27/2023 8:00" "2064.7" "2067.43" "2064.44" "2065.07" "None" GR 0 00:51:24.816 Test file IO (EURUSD,H1) "12/27/2023 7:00" "2065.88" "2066.26" "2064.42" "2064.7" "None" KE 0 00:51:24.816 Test file IO (EURUSD,H1) "12/27/2023 6:00" "2064.6" "2066" "2064.11" "2065.88" "None" NI 0 00:51:24.816 Test file IO (EURUSD,H1) "12/27/2023 5:00" "2065.44" "2066.59" "2064.44" "2064.62" "None" CK 0 00:51:24.816 Test file IO (EURUSD,H1) "12/27/2023 4:00" "2066.74" "2067.28" "2064.8" "2065.44" "None" HO 0 00:51:24.816 Test file IO (EURUSD,H1) "12/27/2023 3:00" "2065.58" "2067.89" "2064.95" "2066.74" "None" RQ 0 00:51:24.816 Test file IO (EURUSD,H1) "12/27/2023 2:00" "2066.2" "2066.52" "2063.97" "2065.63" "None" RD 0 00:51:24.816 Test file IO (EURUSD,H1) "12/27/2023 1:00" "2068.08" "2068.62" "2066.05" "2066.2" "None"
Schreiben von Daten/Informationen in die Dateien
Beim Schreiben von Daten in die Dateien wird ein etwas anderer Ansatz verfolgt als beim Lesen.
Wir können eine Methode namens FileWrite verwenden, die Variablen eines beliebigen Datentyps annimmt.
template <typename T> static bool CFileIO::write(int file_handle, T info) { if(FileWrite(file_handle, info) == 0) { printf("%s failed to write to a file. Error = %s", __FUNCTION__, fileErrorsDescription(GetLastError())); return false; } return true; }
Versuchen wir, neue Daten an das Ende einer bestehenden Datei zu schreiben.
Im Dateimodus: r steht für Lesen, + steht für Lesen und Schreiben und a steht für Ergänzen (Anhängen) neuer Informationen am Ende einer Datei.
void OnStart() { CFile f = CFileIO::open("readme.txt", "r+a"); f.write("Newly added data | "+string(TimeLocal())); f.close(); }
Nachdem ich das Skript mehrmals ausgeführt hatte, wurde die Datei readme.txt mit neuen Datenzeilen angezeigt.
hello, this is a readme file with a plenty of information to read from. This is a third line after a space. Newly added data | 2025.12.22 06:32:35 Newly added data | 2025.12.22 06:33:05 Newly added data | 2025.12.22 06:33:19
Die Methode FileWrite kann, wenn sie eine dynamische (Template-Variable) erhält, mit allen außer Array-Variablen arbeiten.
Um Arrays mit Daten in eine Datei zu schreiben, können wir die Funktion FileWriteArray verwenden.
template <typename T> bool CFile::write(T &info[]) { if(!isHandleOk(__FUNCTION__)) return false; //--- if(FileWriteArray(m_handle, info) == 0) { printf("%s failed to write an array to a file. Error = %s", __FUNCTION__, fileErrorsDescription(GetLastError())); return false; } return true; }
Obwohl die Funktion FileWriteArray für Binärdateien gedacht ist, können wir sie dazu zwingen, ein Array in eine Textdatei zu schreiben.
void OnStart() { CFile f = CFileIO::open("array.txt", "wt"); string data[] = {"data01", "data02", "data03", "data04"}; f.write( data); f.close(); }
Ausgabe:
2025.12.22 06:46:16.324 Test file IO (EURUSD,H1) CFile::write<string> failed to write an array to a file. Error = The file must be opened as a text
Wir erhalten die Fehlermeldung, dass unsere Datei als Textdatei geöffnet werden sollte.
Das liegt daran, dass wir zwar in der Lage waren, Textdateien zu lesen und zu schreiben, sie aber nie mit einem FILE_TXT-Flag geöffnet haben; wir haben noch keine Möglichkeit, dies im Dateimodus-Argument zu behandeln.
Für Textdateien müssen wir den Buchstaben 't' akzeptieren, der dann das Flag FILE_TXT auslöst.
int CFileIO::flagsgen(const string file_mode, bool &is_append, bool shared_IO = true) { //--- default flag(s) for txt files int flags = 0; string mode = file_mode; StringToLower(mode); for(int i = 0; i < (int)mode.Length(); i++) { switch(StringGetCharacter(mode, i)) { case 'r': flags |= FILE_READ; if(shared_IO) flags |= FILE_SHARE_READ; break; case 'w': flags |= FILE_WRITE; if(shared_IO) flags |= FILE_SHARE_WRITE; break; //--- other cases case 't': //Additional text mode flags |= FILE_TXT; break; } } return flags; }
Um Fehler wie den obigen zu umgehen, müssen Sie beim Öffnen einer Text- oder textbasierten Datei lediglich das 't' angeben.
void OnStart() { CFile f = CFileIO::open("array.txt", "wt"); string data[] = {"data01", "data02", "data03", "data04"}; f.write( data); f.close(); }
Ausgabe:

Schreiben in eine CSV-Datei
Da eine CSV-Datei eine zweidimensionale Datenspeicherung ist, müssen wir sie anders behandeln, wenn wir neue Daten in sie schreiben wollen. Beim Lesen einer solchen Datei haben wir einen CSV-Reader verwendet; dieses Mal werden wir einen CSV-Writer einsetzen.
Die Klasse benötigt ähnliche Argumente wie der CSV-Reader.
class CSVWriter { protected: int m_handle; string m_delimiter; char m_quote; char m_escape; bool m_doublequote; string EscapeField(const string value); public: CSVWriter(CFile &file, const string delimiter = ",", const char quotechar = '"', const char escapechar = '\\', const bool doublequote = true); bool writeRow(const string &row[]); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CSVWriter::CSVWriter(CFile &file, const string delimiter, const char quotechar, const char escapechar, const bool doublequote) { m_handle = file.getHandle(); m_delimiter = delimiter; m_quote = quotechar; m_escape = escapechar; m_doublequote = doublequote; }
Wir müssen alle erhaltenen Einträge (Felder) entschlüsseln, bevor wir sie sicher in eine CSV-Datei schreiben.
string CSVWriter::EscapeField(const string value) { bool must_quote = false; string out = ""; int len = StringLen(value); for(int i = 0; i < len; i++) { char ch = (char)StringGetCharacter(value, i); // Detect if quoting is needed if(ch == m_quote || ch == '\n' || ch == '\r' || CharToString(ch) == m_delimiter) { must_quote = true; } // Quote escaping if(ch == m_quote) { if(m_doublequote) out += CharToString(m_quote) + CharToString(m_quote); // "" else out += CharToString(m_escape) + CharToString(m_quote); // \" } else { out += CharToString(ch); } } if(must_quote) return CharToString(m_quote) + out + CharToString(m_quote); return out; }
Die Funktion writeRow ist für das Schreiben von Werten in eine CSV-Datei zuständig.
bool CSVWriter::writeRow(const string &row[]) { string line = ""; int cols = ArraySize(row); for(int i = 0; i < cols; i++) { if(i > 0) line += m_delimiter; line += EscapeField(row[i]); } FileWriteString(m_handle, "\n"+line); return true; }
Lassen Sie uns versuchen, neue Zeilen in die Datei mydata.csv einzufügen.
void OnStart() { CFile f = CFileIO::open("mydata.csv","w+a"); CSVWriter writer(f, ","); double open = iOpen(Symbol(), Period(), 0); double high = iHigh(Symbol(), Period(), 0); double low = iLow(Symbol(), Period(), 0); double close = iClose(Symbol(), Period(), 0); string row[] = {string(TimeCurrent()), (string)open, (string)high, (string)low, (string)close}; writer.writeRow(row); f.close(); }
Ausgabe:

Zusätzliche Methoden
Es gibt mehrere nützliche Methoden in Pythons integriertem E/A-Mechanismus, die das Lesen und Schreiben mühelos machen.
Übrigens sind nicht alle Methoden, die hier und im gesamten Artikel aufgeführt sind, von Python übernommen worden; einige sind von der MQL5-Sprache selbst inspiriert.
01: Die Methode read()
Diese Methode wird verwendet, um alles, was in einer Datei steht, als Zeichenkette zu lesen.
with open(f"{files_path}\\readme.txt", "r") as file: print(file.read())
Ausgabe:
(venv) python main.py
hello, this is a readme file with a plenty of information to read from.
This is a third line after a space.In MQL5 lesen wir alle Daten aus einer Textdatei (standardmäßig) und fügen die Werte in eine große Zeichenkette (string) ein, die durch einen neuen Zeilencode („\n“) getrennt ist.
string CFile::read(int size = -1) { if(!isHandleOk(__FUNCTION__)) return ""; //--- string result = ""; if(size < 0) // read entire file { while(!FileIsEnding(m_handle)) { result += FileReadString(m_handle); if(FileIsLineEnding(m_handle)) result += "\n"; } } else { result = FileReadString(m_handle, size); } return result; //but not here }
Beispiel für die Verwendung:
void OnStart() { CFile f = CFileIO::open("readme.txt", "rt"); Print(f.read()); f.close(); }
Ausgabe:
NQ 0 08:32:45.949 Test file IO (EURUSD,H1) hello, this is a readme file with a plenty of information to read from. DQ 0 08:32:45.949 Test file IO (EURUSD,H1) GJ 0 08:32:45.949 Test file IO (EURUSD,H1) This is a third line after a space.
02: Die Methode tell()
Diese Funktion gibt die aktuelle Position des Dateideskriptors in Bytes vom Anfang der Datei zurück.
int CFile::tell() { if(!isHandleOk(__FUNCTION__)) return -1; return (int)FileTell(m_handle); }
03: Die Methode flush()
Schreibt alle im Puffer der Ein- und Ausgabedatei verbleibenden Daten auf eine Festplatte.
void CFile::flush() { if(!isHandleOk(__FUNCTION__)) return; FileFlush(m_handle); }
04: Die Methode seek()
Die Funktion verschiebt die Position des Dateizeigers um eine bestimmte Anzahl von Bytes relativ zur angegebenen Position.
void CFile::seek(const long offset, const ENUM_FILE_POSITION origin) { //--- check handle if (!isHandleOk(__FUNCTION__)) return; FileSeek(m_handle,offset,origin); }
05: Prüfung, ob die Datei lesbar und beschreibbar ist
Diese beiden kleinen Funktionen können uns helfen, bevor wir uns entscheiden, einige Informationen aus und in die Dateien zu lesen oder zu schreiben.
Bei isreadable() wird geprüft, ob das Flag FILE_READ unter den Datei-Flags vorhanden ist.
bool isreadable() { return (m_flags & FILE_READ) != 0; }
Bei iswritable() wird geprüft, ob das Flag FILE_WRITE unter den Datei-Flags vorhanden ist.
bool iswritable() { return (m_flags & FILE_WRITE) != 0; }
Beispiel für die Verwendung:
void OnStart() { CFile f = CFileIO::open("readme.txt", "r"); //open the file in read-only mode printf("Reading a text file line by line...."); string text; while(f.readline(text)) Print(text); Print("is writable: ", f.iswritable()); Print("is readable: ", f.isreadable()); f.close(); //closing after you are done with it }
Ausgabe:
CG 0 16:54:21.159 Test file IO (EURUSD,H1) is writable: false FQ 0 16:54:21.159 Test file IO (EURUSD,H1) is readable: true
Abschließende Überlegungen
Datei-E/A-Operationen müssen nicht immer so kompliziert sein, wie sie in MQL5 oft erscheinen. Dieser Artikel zeigt, dass es mit einer sorgfältigen Abstraktion und einem klaren Designziel möglich ist, einen sauberen, zuverlässigen und Python-ähnlichen Ansatz zum Lesen von und Schreiben in Dateien zu entwickeln und dabei die Einschränkungen der MetaTrader 5-Umgebung zu respektieren.
Wir haben die Grundlagen der Dateieingabe und -ausgabe für gängige Anwendungsfälle wie Text- und CSV-Dateien behandelt, Dateimodi, Kodierungsüberlegungen, das Verhalten beim Anhängen und das sichere Lese-/Schreibmuster untersucht und gezeigt, wie Konstrukte auf höherer Ebene über die nativen MQL5-Dateifunktionen gelegt werden können. Indem wir diese Details in einem wiederverwendbaren Modul kapseln, verringern wir die Anzahl der Standardformulare, minimieren häufige Fehler und erleichtern es, die Dateivorgänge zu verstehen und zu pflegen.
Mit freundlichen Grüßen.
Tabelle der Anhänge
| Dateiname | Beschreibung und Verwendung |
|---|---|
| Include\PyMQL5\fileIO\fileIO.mqh | Enthält sowohl CFile- als auch CFileIO-Klassen für die Arbeit mit allen Dateitypen in MetaTrader 5. |
| Include\PyMQL$\fileIO\csv.mqh | Es hat zwei Klassen, CSVReader und CSVWriter, zum Lesen bzw. Schreiben von CSV-Dateien. |
| Test file IO.mq5 | Ein endgültiges Skript (Spielwiese) für alle in diesem Artikel besprochenen Methoden. |
| Files\* | Sie enthält alle Dateien, die wir zum Testen unseres Codes benötigen. |
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/20695
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Erstellen von nutzerdefinierten Indikatoren in MQL5 (Teil 3): Erweiterungen auf Multi-Messuhren mit Sektor- und Rundstilen
Larry Williams Marktgeheimnisse (Teil 1): Aufbau eines Swing-Struktur-Indikators in MQL5
Larry Williams Marktgeheimnisse (Teil 2): Automatisierung eines Handelssystems der Marktstruktur
Erstellen von nutzerdefinierten Indikatoren in MQL5 (Teil 2): Bau eines RSI-Displays im Stil einer Messuhr mit Leinwand und Nadelmechanik
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.