Von der Grundstufe bis zur Mittelstufe: Direkter Dateizugriff (I)
Einführung
Im vorigen Artikel „Von der Grundstufe zur Mittelstufe: FileSave und FileLoad“ haben wir die Bibliotheksfunktionen FileLoad und FileSave vorgestellt. Obwohl viele Entwickler sie als etwas eingeschränkt betrachten, da bestimmte Vorgänge umständlich sein können, sind sie sehr nützlich, wenn es um die Erstellung von Logdateien geht. Für diejenigen, die mit ihnen nicht vertraut sind: Logdateien helfen uns, zu verstehen, wie sich Code in bestimmten Szenarien verhält, und sind ein äußerst nützliches Werkzeug für jeden Entwickler.
Dennoch sind die Funktionen FileSave und FileLoad hauptsächlich auf Implementierungen ausgerichtet, bei denen der Zugriff auf Dateidaten sequenziell erfolgt. Das ergibt sich aus ihrer Arbeitsweise. Entwickler benötigen oft einen wahlfreien Zugriff auf eine Datei, obwohl FileLoad und FileSave diesen nur indirekt bereitstellen können, indem sie die gesamte Datei in den Arbeitsspeicher laden und wieder speichern.
Daher wird der wahlfreie Zugriff zwar emuliert, tatsächlich erfolgt er jedoch nicht auf die übliche Weise. In diesem Fall ist das Ziel, nur die notwendigen Teile der Datei in kleinen Blöcken zu laden. Obwohl dies unnötig erscheinen mag, da Arbeitsspeicher heute günstig genug ist, um große Dateien aufzunehmen, kann es in vielen anderen Szenarien sehr nützlich sein, in denen das Ziel darin besteht, die Datei in bestimmten Fragmenten zu verarbeiten oder darauf zuzugreifen.
Direkter Dateizugriff (Teil 1)
Wenn Sie die Dokumentation verschiedener Programmiersprachen studieren, deren integrierte Prozeduren und Funktionen untersuchen und dabei besonders auf den Abschnitt zur Dateiverarbeitung achten, werden Sie feststellen, dass sie oft weit mehr Möglichkeiten bieten, als Sie in der Praxis wahrscheinlich benötigen. Einige Sprachen, wie C++, verfügen über eine sehr geringe Anzahl an Funktionen und Prozeduren für die Arbeit mit Dateien.
C++ bietet jedoch erweiterbare Ein-/Ausgabemechanismen, die auch an andere Aufgaben angepasst werden können, was eine ausdrucksstärkere Syntax auf Basis von E/A-Stream-Operatoren ermöglicht. Wir werden vergleichbare Einrichtungen in MQL5 in zukünftigen Artikeln besprechen. Bleiben wir vorerst bei den Grundlagen, da es keinen Sinn ergibt, die Dinge zu früh zu verkomplizieren.
Wenn Sie dann die MQL5-Dokumentation konsultieren, werden Sie feststellen, dass sie viele verschiedene Funktionen und Prozeduren enthält, die sowohl zum Lesen als auch zum Schreiben von Daten in Dateien gedacht sind. Die einfachsten Funktionen sind FileLoad und FileSave, die im vorherigen Artikel eingeführt und erklärt wurden. Wie jedoch bereits in der Einleitung zu diesem Artikel erwähnt, sind diese Funktionen nicht für den wahlfreien Zugriff auf Dateiinhalte gedacht. Deshalb bietet die MQL5-Dokumentation mehrere andere Funktionen, die speziell für den wahlfreien Zugriff auf Dateiinhalte vorgesehen sind.
Dies erklärt teilweise, warum die Dokumentation so viele verschiedene Funktionen beschreibt. Es erklärt jedoch nicht, wie man mit diesen Funktionen arbeitet, noch erklärt es, wie wir mit benutzerdefinierten Typen umgehen können. Ja, lieber Leser, als Programmierer sind Sie nicht darauf beschränkt, nur mit den von der Programmiersprache definierten Typen zu arbeiten. Gute Programmiersprachen, wie MQL5, erlauben es uns, benutzerdefinierte Typen zu erstellen, die auf spezifische Situationen zugeschnitten sind. Sie verstehen vielleicht nicht ganz, was ich meine, da dies etwas ungewöhnlich erscheinen mag.
In Artikeln aus derselben Serie jedoch, als wir über Templates und Typnamen sprachen, haben wir genau das getan: benutzerdefinierte Typen erstellt, die auf ein bestimmtes Problem zugeschnitten sind. Wir können auch Unions und Strukturen verwenden, um Abstraktionen für spezialisierte Daten zu erstellen.
Wir werden daher eine bewusste Einschränkung vornehmen, um die folgende Diskussion zu vereinfachen. Da das Ziel darin besteht, bestimmte Operationen so klar wie möglich zu demonstrieren, ist es nicht notwendig, jede in der MQL5-Dokumentation beschriebene Dateiverarbeitungsfunktion zu erklären, und wir können uns auf einen einzigen Modellierungsansatz konzentrieren.
Der hier vorgestellte Ansatz sollte keinesfalls als der einzige Weg angesehen werden, dies zu tun. Je nach spezifischer Situation ist die Verwendung einer Funktion oder Prozedur aus der MQL5-Standardbibliothek sicherlich der beste Weg, um bestimmte Arten von Aufgaben zu lösen. Dies liegt daran, dass der Prozess in diesem Fall direkter wird und keine indirekte Umgehungslösung darstellt, die auf eine andere Art von Lösung abzielt.
Dennoch werden wir nur eine kleine Teilmenge der verfügbaren Funktionen verwenden. Dies sollte Sie jedoch nicht davon abhalten, mit anderen Funktionen oder Prozeduren zu experimentieren, um spezifischere Operationen in der MQL5-Standardbibliothek zu verstehen. Versuchen Sie also, lieber Leser, Möglichkeiten jenseits der hier demonstrierten in Betracht zu ziehen. Das gleiche Ergebnis kann oft auf verschiedene Weise erreicht werden, und manche Ansätze sind für den einen Programmierer möglicherweise leichter zu verstehen als für einen anderen. Beginnen wir also mit dem unten stehenden Code:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. int handle; 07. 08. if ((handle = FileOpen("Hello.txt", FILE_WRITE)) == INVALID_HANDLE) 09. { 10. Print("Error..."); 11. return; 12. }; 13. FileWrite(handle, "This file was created by a script written in MQL5."); 14. FileWrite(handle, "New information is being added to the file."); 15. FileWrite(handle, "Version FileWrite..."); 16. FileWrite(handle, "Checking writing values [", 1, "]-[", M_PI, "]"); 17. 18. FileClose(handle); 19. 20. Print("Success"); 21. } 22. //+------------------------------------------------------------------+
Code 01
Wenn wir Code 01 ausführen, werden wir sehen, dass die Datei im entsprechenden Laufzeit-Sandbox-Ordner erstellt wird. Ich glaube, Sie, lieber Leser, sollten keine Schwierigkeiten haben zu verstehen, um welche Datei es sich handelt und wo sie erstellt wird. Ein Detail zum Dateiinhalt ist jedoch besonders wichtig. An dieser Stelle wird es interessant.
Achten Sie auf Folgendes: In Code 01 geben wir in Zeile 08 an, dass die Datei im Schreibmodus geöffnet wird. Wenn der Vorgang erfolgreich ist, erhält die Variable ein gültiges Handle, das verwendet werden kann, um mit der Datei zu arbeiten. Bis zu diesem Punkt war alles sehr einfach. So einfach sogar, dass wir zur Anzeige, dass die Dateioperationen abgeschlossen sind, Zeile 18 verwenden, in der die Datei geschlossen und das Handle wieder freigegeben wird. Was uns jedoch wirklich interessiert, ist der Code zwischen den Zeilen 13 und 16. Diese Zeilen schreiben Daten in die Datei.
Bevor wir uns ansehen, wie das Lesen durchgeführt werden kann, untersuchen wir zunächst den Schreibvorgang, da er weitaus wichtiger ist, als es auf den ersten Blick erscheinen mag.
Wir können deutlich sehen, was wir in die Datei schreiben wollen, da der gesamte Inhalt sehr einfach und verständlich ist. Dennoch stellt sich die Frage: Was wird tatsächlich in der Datei gespeichert? Um diese Frage zu beantworten, können wir uns den Dateiinhalt unten ansehen:

Abbildung 01
Abbildung 01 zeigt das Ergebnis des Schreibens der Daten in die Datei. „Moment, dieser Inhalt sieht etwas seltsam aus.“ Betrachten wir also dieselbe Datei, die in Abbildung 01 gezeigt wird, aber im Binärformat. Sie können es unten sehen:

Abbildung 02
Passen Sie jetzt auf, lieber Leser: Der Inhalt von Abbildung 02 ist exakt derselbe wie der Inhalt von Abbildung 01. In Abbildung 02 können wir jedoch jedes Byte der Datei sehen, das von Code 01 geschrieben wurde. Abbildung 02 enthüllt ein wichtiges Detail, das bestimmen kann, ob wir beim Lesen der Datei mittels wahlfreiem Zugriff den korrekten Wert oder einen völlig falschen erhalten.
Obwohl wir am Anfang der Datei nichts spezifiziert haben, wie in Abbildung 01 und in Code 01 zu sehen ist, sehen wir am Anfang der in Abbildung 02 gezeigten Datei zwei seltsame Bytes, die nicht viel Sinn ergeben. Diese Bytes geben das Dateiformat an und sind für die vorliegende Diskussion nicht relevant. Gleichzeitig zeigt Code 01 deutlich, dass sie nirgendwo explizit angegeben sind. Es gibt hier jedoch auch andere Zeichen, die ebenfalls nicht in Code 01 vorkommen. Dies sind Wagenrücklauf- und Zeilenvorschubzeichen.
In derselben Abbildung 02 sind einige Bytes hervorgehoben. Warum? Sie werden als Tabulator-Trennzeichen eingefügt und trennen die in Zeile 16 von Code 01 geschriebenen Werte. Es ist äußerst wichtig, die Bedeutung dieser Tabulatorzeichen zu verstehen. Je nach Art des geschriebenen Inhalts und vor allem nach dem Zweck, für den die Datei erstellt wird, können diese zusätzlichen Zeichen es unmöglich machen, die Datei später korrekt zu lesen. Sie erwarten möglicherweise nicht, dass diese Zeichen vorhanden sind, und sie könnten das beim Lesen verwendete Indizierungsschema stören.
„In Ordnung, ich verstehe, dass das, was im Code geschrieben steht, nicht immer genau gleich dargestellt wird, wenn es in eine Datei geschrieben wird. Aber ist das immer der Fall?“ Das hängt von der Situation ab, lieber Leser. Beachten Sie, dass das Ergebnis völlig anders ausfallen kann, wenn wir Code 01 in den unten gezeigten Code ändern:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. int handle; 07. 08. if ((handle = FileOpen("Hello.txt", FILE_WRITE | FILE_ANSI)) == INVALID_HANDLE) 09. { 10. Print("Error..."); 11. return; 12. }; 13. FileWrite(handle, "This file was created by a script written in MQL5."); 14. FileWrite(handle, "New information is being added to the file."); 15. FileWrite(handle, "Version FileWrite..."); 16. FileWrite(handle, "Checking writing values [", 1, "]-[", M_PI, "]"); 17. 18. FileClose(handle); 19. 20. Print("Success"); 21. } 22. //+------------------------------------------------------------------+
Code 02
Beachten Sie nun Folgendes: Der einzige Unterschied zwischen Code 01 und diesem Code 02 ist genau das zusätzliche Flag, das an FileOpen übergeben wird. Und genau wegen dieser einfachen Änderung sehen Sie, was passiert, wenn Code 02 die Datei erstellt. Sie können dies sehen, indem Sie Abbildung 03 unten mit Abbildung 02 vergleichen:

Abbildung 03
„Wow! Ich dachte, kleine Änderungen im Code würden das Ergebnis nicht sehr stark beeinflussen.“ Lieber Leser, es gibt Dinge, die man nur dann wirklich verstehen kann, wenn man sie in der Praxis ausprobiert und beobachtet. Das Verhalten, das Sie gerade beobachtet haben, ist ein solches Beispiel. Deshalb empfehle ich, mit dem Material zu experimentieren und es auf leicht unterschiedliche Weise anzuwenden, aber vor allem zu prüfen, was passiert, wenn bestimmte Elemente des Codes geändert werden.
Um beim Thema zu bleiben: Beachten Sie, dass das in Abbildung 03 gezeigte Ergebnis viel näher an dem Inhalt liegt, den wir bei der Betrachtung von Code 02 erwarten würden. Dennoch sind die Tabulator-, Wagenrücklauf- und Zeilenvorschubzeichen immer noch vorhanden. Warum ist das so? Der Grund dafür ist, dass die Datei nicht im Binärmodus geschrieben wird.
Wir werden also eine kleine Änderung am Code vornehmen. Dies ermöglicht es uns, von einer bestimmten Position in der Datei zu lesen, einfach, um ein kleines, aber wichtiges Detail zu verdeutlichen, das Sie verstehen müssen, bevor Sie mit dem Schreiben von Binärdateien fortfahren.
Ohne zu verstehen, wann der Binärmodus und wann der normale Textmodus zu verwenden ist, ist es ziemlich schwierig, in verschiedenen Phasen einer tatsächlichen Implementierung die richtige Wahl zu treffen.
Um deutlich zu sehen, was ich zeigen möchte, müssen wir neuen Code erstellen. Dieser Code ist unten dargestellt.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. int handle; 07. 08. if ((handle = FileOpen("Hello.txt", FILE_WRITE | FILE_READ | FILE_ANSI)) == INVALID_HANDLE) 09. { 10. Print("Error..."); 11. return; 12. }; 13. FileWrite(handle, "This file was created by a script written in MQL5."); 14. FileWrite(handle, "New information is being added to the file."); 15. FileWrite(handle, "Version FileWrite..."); 16. FileWrite(handle, "Checking writing values [", 1, "]-[", M_PI, "]"); 17. 18. FileFlush(handle); 19. 20. FileSeek(handle, 0, SEEK_SET); 21. while (!FileIsEnding(handle)) 22. Print(FileReadString(handle)); 23. 24. FileClose(handle); 25. } 26. //+------------------------------------------------------------------+
Code 03
Code 03 ermöglicht es uns nicht nur, die Datei genauso zu schreiben wie Code 02, sondern auch, den Dateiinhalt zu lesen. Um zu verstehen, was als Nächstes kommt, müssen wir zunächst verstehen, was wir von der Ausführung von Code 03 erwarten können.
Im Wesentlichen wird die Datei geschrieben, die wir in Abbildung 03 sehen. Um sicherzustellen, dass die Datei tatsächlich die erwarteten Informationen enthält, verwenden wir Zeile 18, um den Puffer auf die Festplatte zu schreiben. Der in Zeile 18 gezeigte Vorgang ist möglicherweise zu bestimmten Zeiten unnötig. Je nachdem, wie stark das Betriebssystem mit Lese- oder Schreibvorgängen auf der Festplatte ausgelastet ist, können die durch die Zeilen 13 bis 16 ausgeführten Schreibvorgänge sofort auf die Festplatte geschrieben werden. Dies ist jedoch möglicherweise irgendwann nicht der Fall. Da ich die Datei nicht schließen und dann wieder öffnen möchte, um die gespeicherten Daten zu lesen, verwenden wir diese Bibliotheksfunktion in Zeile 18, um das sofortige Schreiben der Daten in die Datei zu erzwingen.
Sobald dies erledigt ist, können wir uns ziemlich sicher sein, dass die Datei die in Abbildung 03 gezeigten Daten enthält. Nun kommen wir zum Lesen. Da der Dateizeiger nun am Ende positioniert ist, müssen wir ihn woanders hinbewegen. In diesem Fall lassen wir ihn auf den Anfang der Datei zeigen. Dazu verwenden wir Zeile 20, in der wir den Lese-/Schreibzeiger an den Anfang der Datei bewegen. Achten Sie darauf, lieber Leser. Der Lesevorgang, den wir durchführen werden, erfolgt nicht aus dem Pufferinhalt, sondern aus der Datei auf der Festplatte selbst.
Das gleiche Prinzip gilt sowohl für das Schreiben als auch für das Lesen: Wir können die in Zeile 21 gezeigte Lesefunktion verwenden, bei der das Lesen fortgesetzt wird, bis das Ende der Datei erreicht ist. Was uns hier wirklich interessiert, ist, was Zeile 22 bewirkt. In dieser Zeile weisen wir die Anwendung an, den aus der Datei gelesenen Inhalt im MetaTrader 5-Terminal auszugeben. Dies ist der Teil, der uns hier am meisten interessiert.
Achten Sie nun darauf: In unserem Fall haben wir vier Zeilen geschrieben. Sie sind in Abbildung 01 zu sehen. Wenn also Zeile 22 von Code 03 ausgeführt wird, erwarten wir offensichtlich eine Terminalausgabe ähnlich wie in Abbildung 01. Wenn wir uns jedoch das MetaTrader 5-Terminal ansehen, werden wir etwas Ähnliches wie in Abbildung 04 unten sehen:

Abbildung 04
„Hmm, seltsam. Anscheinend werden die ersten drei Zeilen korrekt angezeigt. Der rot markierte Bereich zeigt jedoch Daten, die von den erwarteten abweichen. Sehr seltsam.“ Nun, lieber Leser, obwohl es so scheinen mag, ist das Ergebnis für unsere Zwecke tatsächlich nützlich. Ich möchte jedoch alle daran erinnern, dass wir dies hier auf die einfachste Weise tun, um zu untersuchen, was die meisten Entwickler natürlich versuchen würden, was aber letztendlich zu Ergebnissen führen würde, die von den erwarteten abweichen.
Dieses unerwartete Ergebnis wird durch das Tabulatorzeichen verursacht. „Moment mal, aber wie ist das möglich?“ Nun, lieber Leser, weil FileReadString in diesem nicht-binären Lesemodus anhält, sobald es auf eine Wagenrücklauf-/Zeilenvorschubsequenz oder ein Tabulatorzeichen stößt. Sie müssen nur die Dokumentation für Details konsultieren, obwohl sie die Verwendung von CSV-Dateien erwähnt. Das ist hier nicht der Fall, da der Dateiinhalt nicht dazu gedacht ist, dem CSV-Format zu entsprechen.
Nun, wieder einmal gibt es hier etwas zu lernen. Dies geschieht,weil wir unter den Flags zum Einlesen der Datei nicht festgelegt haben, wie die Daten beim Einlesen interpretiert werden sollen, sodass der Vorgang in Zeile 22 Tabulatorzeichen als Trennzeichen behandelt. Um dieses Problem zu lösen, müssen wir den Code daher noch einmal ändern und die unten gezeigte Version verwenden.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. int handle; 07. 08. if ((handle = FileOpen("Hello.txt", FILE_WRITE | FILE_READ | FILE_ANSI | FILE_TXT)) == INVALID_HANDLE) 09. { 10. Print("Error..."); 11. return; 12. }; 13. FileWrite(handle, "This file was created by a script written in MQL5."); 14. FileWrite(handle, "New information is being added to the file."); 15. FileWrite(handle, "Version FileWrite..."); 16. FileWrite(handle, "Checking writing values [", 1, "]-[", M_PI, "]"); 17. 18. FileFlush(handle); 19. 20. FileSeek(handle, 0, SEEK_SET); 21. while (!FileIsEnding(handle)) 22. Print(FileReadString(handle)); 23. 24. FileClose(handle); 25. } 26. //+------------------------------------------------------------------+
Code 04
Beachten Sie, dass wir, wie bereits erwähnt, nur ein Element im Code ändern müssen: Dies ist Zeile 08, die Sie in Code 04 sehen können. Dennoch unterscheidet sich das Ergebnis stark von dem, was in Abbildung 04 gezeigt wurde. In diesem Fall zeigt das Terminal nach der Ausführung von Code 04 Folgendes an:

Abbildung 05
Bitte beachten Sie, dass die Ausgabe in diesem Fall sehr nahe an dem liegt, was wir von den Schreibanweisungen erwartet haben. Natürlich haben wir ein kleines Problem, das in Abbildung 05 zu sehen ist. Dies ist jedoch ein zweitrangiges Problem, da das Ziel als erreicht betrachtet werden kann. Das Ziel war genau, die Datei so zu lesen, dass etwas sehr Ähnliches zu dem Text erhalten wird, der zwischen den Zeilen 13 und 16 von Code 04 erscheint.
Sehr gut. Nachdem dies nun erledigt und visuell dargestellt wurde, können wir zum binären Lesen und Schreiben übergehen. Dies ermöglicht es uns, zum wahlfreien Zugriff auf Dateiinhalte überzugehen. Da der geschriebene und gelesene Inhalt jedoch gewöhnlichem Text ähnelt, ist er nicht sehr gut für eine klare und einfache Demonstration geeignet, wie binäre Operationen und Operationen mit direktem Zugriff in der Praxis durchgeführt werden. Als ersten Schritt zum Verständnis des Themas müssen wir den Inhalt in etwas Einfacheres ändern. Dies geschieht mit dem unten gezeigten Code.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. int handle; 07. 08. if ((handle = FileOpen("Hello World.txt", FILE_WRITE| FILE_READ | FILE_BIN)) == INVALID_HANDLE) 09. { 10. Print("Error..."); 11. return; 12. }; 13. for (uchar c = 0; c < 10; c++) 14. FileWriteInteger(handle, c, CHAR_VALUE); 15. 16. FileFlush(handle); 17. 18. FileSeek(handle, 0, SEEK_SET); 19. Print("The byte value of the ", FileTell(handle), " position in the file is ", FileReadInteger(handle, CHAR_VALUE)); 20. 21. FileClose(handle); 22. } 23. //+------------------------------------------------------------------+
Code 05
Wenn Code 05 ausgeführt wird, wird eine Datei mit Werten von null bis neun erstellt, wie es in Zeile 14 geschieht. Aber es gibt ein kleines Detail, und es ist äußerst wichtig, dass Sie es gut verstehen. Beachten Sie, dass wir beim Schreiben angeben, dass der zu schreibende Wert vom Typ CHAR_VALUE ist. Dies zu verstehen ist sehr wichtig, da diese Funktion standardmäßig Werte vom Typ INT_VALUE schreibt. „Warum ist das wichtig?“ Nun, um die Bedeutung dieser Information zu verstehen, müssen Sie verstehen, wie Unions funktionieren.
Zwei grundlegende Artikel über Unions wurden in dieser Reihe veröffentlicht, und einer davon ist „Von der Grundstufe bis zur Mittelstufe: Union (I)“. Zusätzlich zu diesem Wissen müssen Sie auch verstehen, wie man mit Arrays arbeitet. Dies wurde ebenfalls in anderen Artikeln aus derselben Reihe erklärt. Da Sie über das gesamte notwendige Wissen verfügen, das in den vorherigen Artikeln beschrieben wurde, können wir versuchen, zu verstehen, warum die Ausführung von Code 05 das unten gezeigte Ergebnis liefert:

Abbildung 06
Wenn Zeile 19 von Code 05 ausgeführt wird, wird etwas Ähnliches wie in Abbildung 06 angezeigt. Aber was bedeutet diese im Terminal ausgegebene Meldung? Wie Sie sehen können, zeigt diese Meldung zwei numerische Werte an. Der erste Wert gibt die aktuelle Position in der Datei an. Der zweite Wert bezieht sich auf den Inhalt dieser spezifischen Position. Hören Sie jetzt gut zu. Mit dieser Lesefunktion sollte die Datei wie ein Array behandelt werden; mit anderen Worten, es ist ein Array, dessen Index automatisch erhöht wird. Wir können auch den Index innerhalb dieses Arrays angeben, und dies geschieht mithilfe von Zeile 18.
Genau wie das Zählen in einem Array bei Index null beginnt, beginnt auch der Index bei null, wenn wir auf Daten in einer Datei zugreifen. Im Gegensatz zu der Situation, in der wir einen Index außerhalb eines Arrays angeben, setzt das System hier bei der Arbeit mit Dateien den Index jedoch immer auf eine gültige Position. Probieren Sie Code 05 aus, um ihn zu verstehen, indem Sie den in der Funktion in Zeile 18 verwendeten Nullwert durch andere Werte ersetzen. Schauen Sie sich das Ergebnis an. Dies wird Ihnen helfen, zu verstehen, wie der Datenzugriff durchgeführt wird.
Aber hier müssen Sie auch einen weiteren Aspekt verstehen. Beachten Sie, dass FileReadInteger ebenfalls CHAR_VALUE verwendet. Dies liegt daran, dass wir ein einzelnes Byte lesen möchten. Normalerweise würden vier Bytes gelesen, was den Index oder die Position in der Datei um eben diese vier Bytes vorrücken würde. Achten Sie sehr genau auf die Details, die ich erwähne. Genau deshalb wird Ihnen das Verständnis von Unions sehr dabei helfen, diese Art des Dateizugriffs nachzuvollziehen.
Bevor Sie fortfahren, testen Sie Code 05 gründlich, bis Sie vollständig verstanden haben, wie er funktioniert, das heißt, bis Sie erkennen, dass Dateien nichts anderes als auf der Festplatte gespeicherte Arrays sind. Sobald Sie dies verinnerlicht haben, wird es für Sie viel einfacher sein, das zu verstehen, was wir als Nächstes tun werden.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. int handle; 07. 08. if ((handle = FileOpen("Hello World.txt", FILE_WRITE| FILE_READ | FILE_BIN)) == INVALID_HANDLE) 09. { 10. Print("Error..."); 11. return; 12. }; 13. for (uchar c = 0; c < 10; c++) 14. FileWriteInteger(handle, c, CHAR_VALUE); 15. 16. FileFlush(handle); 17. 18. FileSeek(handle, 4, SEEK_SET); 19. FileWriteInteger(handle, 45, CHAR_VALUE); 20. 21. FileFlush(handle); 22. 23. FileSeek(handle, 4, SEEK_SET); 24. Print("The byte value of the ", FileTell(handle), " position in the file is ", FileReadInteger(handle, CHAR_VALUE)); 25. 26. FileClose(handle); 27. } 28. //+------------------------------------------------------------------+
Code 06
Sehen Sie sich nun Code 06 an. Wie Sie sehen werden, ist er dem Code 05 sehr ähnlich. Hier tun wir jedoch etwas, das leicht über Code 05 hinausgeht, da wir unter Verwendung eines vollständig wahlfreien Zugriffs in die Datei schreiben. Es gibt hier einen nicht ganz so lustigen Haken, falls es Ihnen noch nicht gelungen ist, das Konzept eines Arrays mit dem Konzept einer Datei zu verknüpfen. Code 06 führt einen Vorgang aus, der mit einem herkömmlichen Array unmöglich wäre, zumindest wenn wir mit Arrays so arbeiten würden, wie dies in den meisten Fällen üblich ist.
Bevor wir Code 06 untersuchen, schauen wir uns das Ergebnis der Ausführung an:

Abbildung 07
Nun kommt der interessanteste Teil: zu verstehen, wie die Meldung in Abbildung 07 erhalten wurde. Beachten Sie zunächst, dass die einzigen Änderungen in Code 06 im Vergleich zu Code 05 in den Zeilen 18 und 19 liegen. Diese beiden Zeilen werden verwendet, um den Wert an einer bestimmten Position in der Datei zu ändern. Dies entspricht der Zuweisung eines neuen Wertes an ein bestimmtes Array-Element. Bis zu diesem Punkt ist alles sehr leicht zu verstehen, da die angegebene Position innerhalb dessen liegt, was durch die Schleife in Zeile 13 erstellt wird.
Sie können dasselbe tun, auch ohne die Position vorher zu erstellen, oder genauer gesagt, Sie müssen nicht jede Position vorab zuweisen, bevor Sie ihr einen Wert zuweisen. Denken Sie daran, dass wir es hier mit Dateien zu tun haben. In diesem Fall können Sie eine Position weit hinter dem aktuellen Ende der Datei angeben, und der Vorgang wird dennoch funktionieren.
Das mag verwirrend klingen. In der Praxis ist alles ganz einfach. Ersetzen Sie den Wert der FileSeek-Funktion in Zeile 18 durch einen Wert, der größer ist als die zehn Elemente, die durch die Schleife in Zeile 13 erstellt wurden, zum Beispiel 14. Vergessen Sie nicht, auch den Wert in Zeile 23 auf denselben Wert zu ändern, der in Zeile 18 verwendet wurde. Das Ausführen dieses Codes führt zu dem unten gezeigten Ergebnis:

Abbildung 08
„Wie seltsam! Ich dachte immer, dass Daten Byte für Byte und Position für Position in eine Datei geschrieben werden müssen. Können wir also überall schreiben, unabhängig davon, welche Daten bereits vorhanden sind?“ Das ist sicherlich merkwürdig. Lieber Leser, Situationen wie diese machen das Programmieren unvergesslich und spannend, auch wenn es für diejenigen, die gerade erst damit vertraut werden, manchmal etwas verwirrend erscheinen mag.
Schlussfolgerung
In diesem Artikel haben wir zum ersten Mal den wahlfreien Zugriff auf Dateiinhalte untersucht. Wenn ich das sage, gehe ich natürlich davon aus, lieber Leser, dass Sie vielleicht nicht erkannt haben, wie viele Feinheiten die Dateiverarbeitung mit sich bringt.
Da wir uns in diesem Artikel hauptsächlich auf das Schreiben an eine beliebige Position innerhalb einer Datei konzentriert haben, hatten wir nicht die Gelegenheit, einige Details zur Indizierung von Daten innerhalb der Datei selbst zu erläutern. Und da dieses Thema sehr wichtig ist, haben wir nun ein natürliches Thema für den nächsten Artikel. Versuchen Sie also, das in diesem Artikel Gezeigte zu lernen und in der Praxis zu vertiefen, indem Sie die im Anhang enthaltenen Quelldateien verwenden. Wir sehen uns im nächsten Artikel wieder.
| MQ5-Datei | Beschreibung |
|---|---|
| Code 01 | Demonstration des Dateizugriffs |
| Code 02 | Demonstration des Dateizugriffs |
| Code 03 | Demonstration des Dateizugriffs |
| Code 04 | Demonstration des Dateizugriffs |
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/16246
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.
Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
Neuronale Netze im Trading: Effektive Merkmalsextraktion zur präzisen Klassifizierung (letzter Teil)
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Gauß-Prozesse im maschinellen Lernen (Teil 1): Klassifikationsmodell in MQL5
- 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.