Einleitung
Text- und Binärdatei
Übersicht Dateistreams
Ausgabestream-Objekt
Schreiben in Datei
Eingabestream-Objekt
Lesen aus Datei
Gleichzeitiges Schreiben und Lesen
Sonstige Dateioperationen
Beispiel und Übung
Genauso wie für die Standard Ein- und Ausgabe die Streams cin bzw. cout verwendet werden, so werden auch für Dateizugriffe unter C++ die Streams ifstream bzw. ofstream eingesetzt. Im Folgenden werden Sie deshalb viele Gemeinsamkeiten zwischen der Standard Ein-/Ausgabe und der Datei Ein-/Ausgabe finden.
Bevor auf die Behandlung von Dateien eingegangen wird, müssen noch zwei Begriffe geklärt werden: den der Textdatei und den der Binärdatei. Die Unterscheidung ist deshalb notwendig, da die beiden Dateitypen unterschiedlich gehandhabt werden.
Zuvor aber noch zwei Hinweise:
|
|
|
|
Textdateien sind Dateien die nur ASCII-Zeichen enthalten. Diese Dateien können mit jedem Editor bearbeitet werden, der 'reine' Textdateien erzeugen kann (z.B. der DOS-Editor EDIT oder auch NOTEPAD). Numerische Daten werden in diesen Dateien ebenfalls in ASCII-Form abgelegt. Beachten Sie bitte, wie die Werte in der Datei abgelegt sind.
| Aktion: Schreiben des Strings "Emil Maier" Schreiben des short-Wertes 100 Schreiben des long-Wertes 0x11223344 Dateiinhalt:
|
|
|
Im Gegensatz dazu sind Binärdateien Dateien, in denen Daten in binärer Form, also in der Form, wie sie im Speicher des Rechners liegen, abgelegt sind. Diese Daten können in der Regel nicht mit einem Editor bearbeitet werden, da Editoren nur Dateien mit ASCII-Zeichen sinnvoll darstellen können. Der Aufbau einer Binärdatei ist, wie bereits erwähnt, aber nicht standardisiert, d.h. unterschiedliche Systeme können Daten unterschiedlich ablegen.
Die Ausgabe von Text in eine Binärdatei unterscheidet sich nicht von der Ausgabe in eine Textdatei. Der Unterschied wird erst bei der Ausgabe von Daten deutlich.
| Aktion: Schreiben des Strings "Emil Maier" Schreiben des short-Wertes 100 Schreiben des long-Wertes 0x11223344 Dateiinhalt:
|
|
|
Um Dateien mithilfe von Streams zu bearbeiten, müssen folgende Schritte durchgeführt werden:
Außerdem müssen Sie, wenn Sie mit Dateistreams arbeiten, Sie die Datei fstream mit #include <fstream> einbinden.
Ein Ausgabestream-Objekt wird mit folgender Anweisung definiert:
| std::ofstream strName; |
ofstream ist die Klasse, über die Dateiausgabe erfolgt und strName ist der Name des Stream-Objekts. Der Name des Stream-Objekts ist frei wählbar. Klassen und Objekte werden zwar erst später behandelt, aber stellen Sie sich unter der obigen Anweisung im Prinzip die Definition einer Variablen strName mit dem Datentyp ofstream vor.
|
|
Das so definiert Stream-Objekt strName besitzt aber noch keine Zuordnung zu irgend einer Datei. Diese Zuordnung erfolgt erst durch den Aufruf der Memberfunktion
| void ofstream::open(const char *pFName, ios::openmode mode=ios::out); |
Der erste Parameter pFName ist ein Zeiger auf den Namen der Datei, mit der der Stream verbunden werden soll.
|
|
Der zweite Parameter mode gibt an, in welchem Modus die Datei geöffnet werden soll. Er ist ein enumerated Datentyp (wird später noch erklärt) und kann eine sinnvolle Kombination aus folgenden Werten sein:
| mode | Bedeutung |
| ios::out | Öffnen einer Datei zum Schreiben (default). |
| ios::trunc | Öffnet eine Datei, wobei der ursprüngliche Inhalt wird verworfen (default). |
| ios::ate | Öffnen einer Datei und positionieren des Schreibzeigers auf das Dateiende. Der Schreibzeiger kann bei Bedarf neu positioniert werden (seekp(...)) und die Daten werden dann an der neuen Position eingefügt. |
| ios::app | Öffnen einer Datei und positionieren des Schreibzeigers auf das Dateiende. Die neuen Daten werden immer ans Ende der Datei eingefügt, unabhängig davon ob der Schreibzeiger inzwischen neu positioniert wurde. |
| ios::binary | Öffnet Datei im Binärmodus. Ohne diese Angabe wird die Datei als Textdatei interpretiert. |
Sie können beim Aufruf der Memberfunktion open(...) auch den zweiten Parameter ganz weglassen; in diesem Fall wird die Datei für die Ausgabe im Textmodus geöffnet und der ursprüngliche Inhalt wird verworfen.
|
|
Es gibt auch noch eine zweite Möglichkeit, einen Ausgabestream zu definieren und dabei gleichzeitig mit einer Datei zu verbinden.
| ofstream myFile(const char* pFName, ios::openmode mode = ios::out); |
Hier wird beim Definieren des Stream-Objekts die zu öffnende Datei und der Mode gleich mit angegeben.
Sehen wir uns beide Möglichkeiten nun anhand eines Beispiels einmal an.
// 1. Möglichkeit #include <fstream> using namespace std; ... // main() Funktion int main () { // Stream-Objekt definieren ofstream myFile; // und anschliessend mit Datei verbinden myFile.open ("d:\\tmp\\test.dat"); ... } |
// 2. Möglicheit #include <fstream> using namespace std; ... // main() Funktion int main () { // Stream-Objekt definieren und gleich mit Datei verbinden ofstream myFile ("d:/tmp/test.dat", ios::out|ios::binary); ... } |
Im ersten Fall wird zunächst das Stream-Objekt definiert und dann mit open(...) mit einer Datei verbunden. Beachten Sie, dass vor dem Namen der Memberfunktion der Name des Stream-Objekts und ein Punkt stehen. Wie Sie später noch sehen werden, ist dies die allgemeine Syntax zum Aufruf einer Memberfunktion für ein bestimmtes Objekt.
Im zweiten Fall wird das Stream-Objekt bei seiner Definition gleich mit einer Datei verbunden. Im Beispiel wird die Datei jetzt im Binärmodus geöffnet. Beachten Sie bitte, dass Sie hier auch den Modus ios::out explizit angeben müssen.
|
|
Da es nicht ausgeschlossen ist, dass beim Öffnen einer Datei auch einmal etwas schief geht, sollten Sie nach der Verbindung mit der Datei auch eventuell aufgetretene Fehler abfangen. Wie Sie dies bei Streams durchführen ist nachfolgend dargestellt.
#include <fstream> using namespace std; ... // main() Funktion int main () { // Stream-Objekt definieren und anschliessend mit Datei verbinden ofstream myFile; myFile.open ("d:/tmp/test.dat"); // Abfrage auf Fehler if (!myFile) { ... // Hier Fehlerbehandlung durchführen } } |
Diese if-Abfrage (die im Detail später noch behandelt wird) ist sowohl bei Text- wie auch bei Binärdateien einzusetzen, aber erst nachdem eine Datei mit einem Stream-Objekt verbunden wurde. Beachten Sie das kleine Ausrufzeichen vor dem Objekt-Namen, dies ist der NOT-Operator. Sie können sich die Funktionsweise des NOT-Operators auch nochmals ansehen.
Nach dem die (gleich noch beschriebene) Bearbeitung der Datei abgeschlossen ist, müssen Sie die Verbindung des Streams zur Datei wieder aufheben. Hierbei gibt es ebenfalls zwei verschiedene Verfahren.
Im ersten Fall heben Sie die Verbindung zur Datei einfach durch den Aufruf der Memberfunktion
| void ofstream::close(); |
wieder auf. Das Stream-Objekt besteht danach weiter und Sie können mittels open(...) eine weitere Datei mit dem gleichen Stream-Objekt verbinden.
#include <fstream> using namespace std; ... // main() Funktion int main () { // Stream-Objekt definieren und mit Datei verbinden ofstream myFile; myFile.open ("d:/tmp/test.dat"); ... // Dateiverbindung aufheben myFile.close(); ... // Stream erneut mit einer Datei verbinden myFile.open("c:/anydir/anotherfile.dat); } |
Die zweite Möglichkeit besteht darin, das Stream-Objekt zu löschen. Wenn Sie ein Stream-Objekt (wie im Beispiel unten) innerhalb eines Blocks definieren (Blöcke werden später noch genauer behandelt), so kann zunächst die Datei bei der Definition des Stream-Objekts mit diesem verbunden werden. Wird am Blockende das Stream-Objekt dann ungültig (gelöscht), so wird automatisch auch die Verbindung zur Datei aufgehoben. Mehr zur Gültigkeit von Variablen und Objekt später in der Lektion Gültigkeitsbereiche im Kapitel Daten II & Funktionen.
#include <fstream> using namespace std; ... // main() Funktion int main () { ... // Blockbeginn mit Klammerung { ofstream myFile("d:/tmp/test.dat"); ... } // Blockende bei schliessender Klammer // Hier wird das Stream-Objekt gelöscht! ... } |
Das Schreiben in eine Textdatei erfolgt prinzipiell gleich wie die Ausgabe auf die Standardausgabe. Anstelle des Stream-Objekts cout wird der Name des Dateistream-Objekts angegeben. Alle zu schreibenden Daten werden, genauso wie bei cout, durch den Operator << in den Stream übertragen. Beachten Sie aber beim Schreiben von Daten, dass Sie diese durch ein Trennzeichen (Leerzeichen, Zeilenvorschub o.Ä.) voneinander trennen müssen, da die Daten sonst später nicht mehr eingelesen werden können.
Dadurch, dass sowohl cout wie auch ofstream die gleiche Basisklasse basic_ostream besitzen, stehen für die Dateiausgabe auch die gleichen Manipulatoren, wie z.B. hex, setw(...) oder setprecision(...), zur Verfügung.
#include <fstream> using namespace std; // Variablen definieren int var1 = 10, var2 = 20; // main() Funktion int main () { // Stream-Objekt definieren und mit Datei verbinden ofstream myFile; myFile.open ("d:/tmp/test.dat"); // Wenn Stream ohne Fehler mit Datei verbunden if (myFile) { // Daten schreiben myFile << var1 << ' '; myFile << setw(5) << var2 << endl; // Datei schliessen myFile.close(); } } |
Sollen numerische Daten binär in einer Datei abgelegt werden, so müssen Sie beim Verbinden der Datei mit den Stream-Objekt die Modes ios::out | ios::binary angeben.
|
|
Das Schreiben eines einzelnen Bytes erfolgt mit der Memberfunktion
| ostream& put(char data); |
data ist das zu schreibende Byte.
|
|
Sollen Daten die aus mehreren Bytes bestehen (short, long usw.) in einer binären Datei abgelegt werden, so wird hierfür die Memberfunktion
| ostream& write(const char *pBuffer, streamsize bytes); |
eingesetzt. pBuffer ist ein const char-Zeiger auf den Beginn des Datenblocks, der in die Datei geschrieben werden soll und bytes gibt die Anzahl der zu schreibenden Bytes an. Da die Memberfunktion einen const char-Zeiger erwartet, müssen Sie in der Regel eine Typkonvertierung vornehmen, wenn Sie numerische Daten abspeichern (siehe Beispiel). Vergessen Sie diese Typkonvertierung, so erhalten Sie vom Compiler eine Fehlermeldung.
#include <fstream> using namespace std; // Variablen definieren int var1 = 10, var2 = 20; // main() Funktion int main () { // Stream-Objekt definieren und mit Datei verbinden ofstream myFile; myFile.open ("d:/tmp/test.dat", ios::out|ios::binary); // Wenn Stream ohne Fehler mit Datei verbunden if (myFile) { // Daten schreiben myFile.write(reinterpret_cast<char*>(&var1), sizeof(var1)); myFile.write(reinterpret_cast<char*>(&var2), sizeof(var2)); // Datei schliessen myFile.close(); } } |
|
|
Beim Schreiben von Daten in Binärdateien können die Daten unmittelbar hintereinander geschrieben werden, d.h. es sind keine Trennzeichen wie bei einer Textdatei notwendig, um später die Daten wieder einlesen zu können.
Ein Eingabestream-Objekt wird mit folgender Anweisung definiert:
| ifstream strName; |
ifstream ist der Stream, der für das Einlesen von Daten aus einer Datei eingesetzt wird und strName der (beliebige) Name des Stream-Objekts. Das so definierte Stream-Objekt strName besitzt aber ebenfalls noch keine Zuordnung zu irgend einer Datei.
Nach dem das Stream-Objekt erstellt, ist kann es mit einer Datei verbunden werden. Dies erfolgt wieder durch den Aufruf der Memberfunktion
| void ifstream::open(const char *pFName, ios::openmode mode=ios::in); |
Der erste Parameter pFName ist ein char-Zeiger auf den Namen der zu öffnenden Datei und der zweite Parameter mode gibt an, in welchem Modus die Datei zu öffnen ist. Er kann hier eine sinnvolle Kombination aus folgenden Werten sein:
| mode | Bedeutung |
| ios::in | Öffnen einer Datei zum Lesen |
| ios::binary | Öffnet Datei im Binärmodus. Ohne diese Angabe wird die Datei als Textdatei interpretiert. |
Sie können beim Aufruf der Memberfunktion auch diesen zweiten Parameter weglassen; in diesem Fall wird die Datei für die Eingabe im Textmodus geöffnet.
Und auch hier besteht die Möglichkeit, einen Eingabestream gleich bei seiner Definition mit einer Datei zu verbinden.
| ifstream myFile(const char* pFName, ios::openmode mode = ios::in); |
Hier wird bei der Definition des Stream-Objekts die zu öffnende Datei und der Mode gleich mit angegeben.
Sehen wir uns beide Möglichkeiten anhand eines Beispiels einmal an.
// 1. Möglichkeit #include <fstream> using namespace std; ... // main() Funktion int main () { // Stream-Objekt definieren ifstream myFile; // und anschliessend mit Datei verbinden myFile.open ("d:/tmp/test.dat"); // Fehlerabfrage! if (!myFile) { ... // Fehlerbehandlung } ... } |
// 2. Möglichkeit #include <fstream> using namespace std; ... // main() Funktion int main () { // Stream-Objekt definieren und gleichzeitig mit Datei verbinden ifstream myFile("d:/tmp/test.dat", ios::in|ios::binary); // Fehlerabfrage! if (!myFile) { ... // Fehlerbehandlung } ... } |
Im ersten Fall wird zunächst das Stream-Objekt definiert und dann mit open(...) mit einer Datei verbunden.
Im zweiten Fall wird das Stream-Objekt bei seiner Definition gleich mit einer Datei verbunden. Im Beispiel wird die Datei im Binärmodus geöffnet. Beachten Sie bitte, dass Sie hier auch den Modus ios::in explizit angeben müssen.
Und auch das Öffnen einer Datei zum Lesen kann natürlich schief gehen. Prüfen Sie deshalb immer ab, ob die Datei erfolgreich mit dem Stream verbunden werden konnte. Stellen Sie sich einmal vor, Sie wollen von einer Datei einlesen die es gar nicht gibt!
Für das Aufheben der Dateiverbindung und das Löschen des Stream-Objekts gilt das Gleiche wie beim Ausgabestream.
Das Lesen aus einer Textdatei erfolgt auch hier prinzipiell gleich wie das Lesen von Daten von der Standard-Eingabe mit cin. Anstelle des Streamobjekts cin steht hier lediglich der Name des Eingabestream-Objekts. Alle zu lesenden Daten werden ebenfalls durch den Operator >> aus dem Stream ausgelesen.
#include <fstream> using namespace std; // Variablen definieren int var1, var2; // main() Funktion int main () { // Stream-Objekt definieren und mit Datei verbinden ifstream myFile; myFile.open ("d:/tmp/test.dat"); // Hier Fehler abfangen! // Daten lesen myFile >> var1 >> var2; // Datei schliessen myFile.close(); } |
Da sowohl cin wie auch ifstream die gleiche Basisklasse basic_istream besitzen, stehen für die Dateieingabe auch die gleichen Manipulatoren, wie z.B. hex oder oct, zur Verfügung.
Sollen numerische Daten aus einer Binärdatei ausgelesen werden, so müssen Sie beim Verbinden der Datei mit den Stream-Objekt den Mode ios::in | ios::binary angeben.
Das Lesen eines einzelnen Bytes erfolgt mit der Memberfunktion
| istream& get(char& data); |
Das gelesene Byte ist nach der Ausführung der Memberfunktion in der Variable data abgelegt. Beachten Sie, dass nach dem Datentyp char der Operator & steht. Dies ist hier nicht der Adressoperator, sondern der später bei den Funktionen noch aufgeführte Referenzoperator. In ihrem Programm übergeben Sie die einzulesende Variable 'ganz normal' an die Memberfunktion, also z.B. myFile.get(charVar);.
Sollen Daten die aus mehreren Bytes bestehen (short, long usw.) aus einer Binärdatei ausgelesen werden, so wird hierfür die Memberfunktion
| istream& read(char *pBuffer, streamsize bytes); |
verwendet. pBuffer ist ein char-Zeiger auf den Beginn des Datenblocks, in dem die aus der Datei ausgelesenen Daten abgelegt werden und bytes gibt die Anzahl der zu lesenden Bytes an. Da die Memberfunktion wiederum einen char-Zeiger erwartet, müssen Sie in der Regel eine Typkonvertierung verwenden wenn Sie numerische Daten einlesen (siehe Beispiel).
#include <fstream> using namespace std; // Variablen definieren int var1, var2; // main() Funktion int main () { // Stream-Objekt definieren und mit Datei verbinden ifstream myFile; myFile.open ("d:/tmp/test.dat", ios::in|ios::binary); // Hier Fehler abfangen! // Daten lesen myFile.read(reinterpret_cast<char*>(&var1), sizeof(var1)); myFile.read(reinterpret_cast<char*>(&var2), sizeof(var2)); // Datei schliessen myFile.close(); } |
Und auch dies ist möglich: Sie können einen Dateistreams zum gleichzeitigen Lesen und Schreiben öffnen. Hierzu benötigen Sie ein Stream-Objekt vom Typ fstream das Sie wie folgt definieren:
| fstream strName; |
fstream ist der Stream, der für das Ausgabe und das Einlesen von Daten aus einer Datei eingesetzt wird und strName der (beliebige) Name des Stream-Objekts. Das so definierte Stream-Objekt strName besitzt aber ebenfalls noch keine Zuordnung zu irgend einer Datei. Um das Stream-Objekt mit einer Datei zu verbinden, verwenden Sie entweder die bereits bekannte open(...) Memberfunktion oder aber Sie geben bei der Definition des Stream-Objekts gleich den Dateinamen mit an (genauso wie beim ifstream bzw. ofstream). Lediglich für den Parameter mode sind hier andere Kombinationen zugelassen:
| mode | Bedeutung |
| ios:in | Öffnen einer Datei zum Schreiben |
| ios:out | Öffnen einer Datei zum Lesen |
| ios::binary | Öffnet Datei im Binärmodus. Ohne diese Angabe wird die Datei als Textdatei interpretiert. |
| ios:app | Öffnen einer Datei und positionieren des Schreibzeigers auf das Dateiende. Die neuen Daten werden immer ans Ende der Datei eingefügt, unabhängig davon ob der Schreibzeiger inzwischen neu positioniert wurde. |
| ios::ate | Öffnen einer Datei und positionieren des Schreibzeigers auf das Dateiende. Der Schreibzeiger kann bei Bedarf neu positioniert werden (seekp(...)) und die Daten werden dann an der neuen Position eingefügt. |
| ios::trunc | Öffnet eine Datei, wobei der ursprüngliche Inhalt wird verworfen. |
Beachten Sie, dass Sie den mode-Parameter hier immer angegeben müssen. Wenn Sie die Datei nur mit Lesezugriff öffnen, muss die Datei natürlich bereits existieren.
|
|
Das Lesen, Schreiben und Schließen erfolgt mit den bereits in den vorherigen Abschnitten erläuterten Memberfunktionen.
|
|
In der Praxis werden Sie in vielen Fällen nicht von vornherein wissen, wie viele Daten in einer Datei abgelegt sind. Wenn Sie nun Daten aus einer Datei einlesen, müssen Sie also irgend wie feststellen können, ob das Dateiende erreicht ist und somit alle Daten eingelesen wurden. Die Abfrage ob das Dateiende erreicht wurde erfolgt mit der Memberfunktion
| bool basic_ios::eof() |
Diese Memberfunktion liefert den Wert true zurück wenn das Dateiende erreicht ist.
... // Stream-Objekt definieren und mit Datei verbinden ifstream myFile("d:/test.dat"); // Einlesen aus Datei myFile >> data1 >> ...; // Prüfen, ob Dateiende noch nicht erreicht if (!myFile.eof()) { ... // Daten aus Datei auswerten } ... |
Beachten Sie, dass weitere Einleseversuche nach dem Erkennen des Dateiendes zwar in der Regel nicht zu einem Programmabsturz führen, jedoch ungültige Daten zurückliefern. Damit eof() das Erreichen des Dateiendes feststellen kann, muss vorher ein Einleseversuch stattgefunden haben.
Die Memberfunktion ignore(...), welche schon in der Lektion Eingabestream cin erläutert wurde, kann zum Überspringen von Daten aus Dateien verwendet werden.
#include <fstream> #include <iostream> // main() Funktion int main() { // Feld zum Einlesen eines Strings char array[100]; // Stream-Objekt definieren und mit Datei verbinden std::ifstream infile("test.dat"); // Wenn Datei geöffnet werden konnte if (infile) { // Erstes Wort einlesen infile >> array; std::cout << "1. Wort: " << array << std::endl; // Rest der 1. Zeile (max. 100 Zeichen) ueberspringen infile.ignore(100,'\n'); // Erstes Wort der 2. Zeile einlesen infile >> array; std::cout << "2. Wort: " << array << std::endl; } } |
Bei Dateistreams lassen sich die internen Dateizeiger (oder genau genommen sind es die Pufferzeiger) beeinflussen, um einen wahlfreien Zugriff auf die in der Datei abgelegten Daten zu erhalten. Hierzu werden die beiden Memberfunktionen
| basic_istream& seekg(ios::off_type offset, ios_base::seek_dir
dir); basic_ostream& seekp(ios::off_type offset, ios_base::seek_dir dir); |
Die Memberfunktion seekg(...) dient zum Positionieren des Lese-Dateizeigers und die Memberfunktion seekp(...) zum Positionieren des Schreib-Dateizeigers, d.h. beide Dateizeiger lassen sich unabhängig voneinander positionieren. offset gibt die Anzahl der Bytes an, um die der Dateizeiger von der Position dir aus bewegt werden soll. Für dir ist einer der folgenden Werte zulässig:
| Wert | Bedeutung |
| ios::cur | Dateizeiger ab der aktuellen Position verschieben |
| ios::beg | Dateizeiger ab Dateianfang verschieben |
| ios::end | Dateizeiger auf Dateiende verschieben |
Beachten Sie bitte, dass der Dateizeiger bei Angabe von ios::beg nur in positive Richtung und bei Angabe von ios::end nur in negative Richtung verschoben werden kann!
Im nachfolgenden Beispiel werden zunächst die Werte 1...5 in eine Binärdatei, die zum Lesen und Schreiben geöffnet ist, geschrieben. Anschließend werden mittels den erwähnten seek Memberfunktionen verschiedene Werte aus der Datei ausgelesen. Beachten Sie dazu die Kommentare im Listing. Zum Schluss wird der vorletzte Wert verändert und dann alle Daten zur Kontrolle nochmals ausgegeben. Beachten Sie bitte, welche Definitionen Sie aus dem std-Namensraum einbinden müssen!
// Beispiel zu wahlfreien Dateizugriff #include <fstream> #include <iostream> // Bezeichner aus std-Namensraum einbinden using std::cout; using std::endl; using std::fstream; using std::ios; // Dateiname const char* const pFILENAME = "test.dat"; // Das zu schreibende und lesende Datum short var; // main() Funktion int main() { // Stream-Objekt definieren und mit binärer Datei verbinden fstream inOutFile(pFILENAME,ios::binary|ios::in|ios::out|ios::trunc); // Fehler abfangen if (!inOutFile) { cout << "Fehler beim Öffnen der Datei " << pFILENAME << endl; exit (1); } // Werte 1...5 in Datei schreiben // Mit einer Schleife geht das später einfacher! var = 1; inOutFile.write (reinterpret_cast<char*>(&var),sizeof(var)); var++; inOutFile.write (reinterpret_cast<char*>(&var),sizeof(var)); var++; inOutFile.write (reinterpret_cast<char*>(&var),sizeof(var)); var++; inOutFile.write (reinterpret_cast<char*>(&var),sizeof(var)); var++; inOutFile.write (reinterpret_cast<char*>(&var),sizeof(var)); // Auf Dateianfang zurück inOutFile.seekg(static_cast<ios::off_type>(0),ios::beg); // 1. Datum einlesen inOutFile.read(reinterpret_cast<char*>(&var),sizeof(var)); cout << "1. Wert: " << var << endl; // Auf letzten Wert positionieren inOutFile.seekg(-static_cast<ios::off_type>(sizeof(var)),ios::end); inOutFile.read(reinterpret_cast<char*>(&var),sizeof(var)); cout << "Letzter Wert: " << var << endl; // Auf vorletzten Wert positionieren inOutFile.seekg(-static_cast<ios::off_type>(sizeof(var)*2),ios::end); inOutFile.read(reinterpret_cast<char*>(&var),sizeof(var)); cout << "Vorletzter Wert: " << var << endl; // Vorletzten Wert überschreiben cout << "Überschreibe jetzt vorletzten Wert!" << endl; var = -1; inOutFile.seekp(-static_cast<ios::off_type>(sizeof(var)*2),ios::end); inOutFile.write(reinterpret_cast<char*>(&var),sizeof(var)); // Und nun alle Werte auslesen cout << "Datei enthält jetzt:\n"; // Lesezeiger zuerst auf Dateianfang inOutFile.seekg(static_cast<ios::off_type>(0),ios::beg); // Die nachfolgende while-Schleife wird später noch behandelt. // Nun alles wieder einlesen bis zum Dateiende do { inOutFile.read(reinterpret_cast<char*>(&var),sizeof(var)); // Wenn nicht Dateiende erreicht, eingelesen Wert ausgeben if (!inOutFile.eof()) cout << var << " "; } while(!inOutFile.eof()); cout << endl; } |
|
1. Wert: 1 |
|
|
Kundendaten: |
|
// Beispiel zum Dateihandling mittels Streams // Dateien einbinden #include <iostream> #include <fstream> using namespace std; // Dateiname const char* const pszDATEI = "kunden.dat"; // main() Funktion int main() { // Definition der Kundendaten mit Initialisierung short kdNummer = 123; double umsatz = 12.3f; short tag=10,monat=5,jahr=2008; // Das Ausgabestream-Objekt wird innerhalb eines Blocks // definiert. Am Blockende wird es gelöscht und damit // die Datei geschlossen! { // Ausgabestream-Objekt definieren und mit Datei verbinden ofstream outFile(pszDATEI); // Fehler abfangen if (!outFile) { cout << "Fehler beim Erstellen der Datei!\n"; exit (1); } // Daten in Datei schreiben // ACHTUNG! Nach jedem Datum entweder ein Leerzeichen oder ein // Zeilenvorschub eingeben damit Daten nachher auch wieder eingelesen können outFile << kdNummer << " " << umsatz << endl; outFile << tag << " " << monat << " " << jahr << endl; } // Blockende, Datei ist wieder geschlossen! // Variablen zur Kontrolle mit 0 belegen kdNummer = tag = monat = jahr = 0; umsatz = 0.0; // Eingabestream-Objekt definieren und jetzt mit open() mit // der Datei verbinden. Möglich wäre auch die Datei direkt // wie vorhin mit dem Stream zu verbinden ifstream inFile; inFile.open(pszDATEI); // Fehler abfangen! if (!inFile) { cout << "Fehler beim Öffnen der Datei!\n"; exit(2); } // Daten aus Datei auslesen inFile >> kdNummer >> umsatz; inFile >> tag >> monat >> jahr; // Dateiverbindung aufheben inFile.close(); // Eingelesene Daten formatiert auf Bildschirm ausgeben cout << "Kundendaten:\n------------\n"; cout << "KD-Nummer : " << kdNummer << endl; cout << "Umsatz in TSD : " << umsatz << endl; cout << "Letzter Umsatz am: "; cout << tag << "." << monat << "." << jahr << endl; } |
Wandeln Sie das obige Beispiel so ab, dass die Daten nun in einer binären(!) Datei abgelegt werden. Verwenden Sie hierfür ein Stream-Objekt das 'gleichzeitiges' Lesen und Schreiben zulässt.
Legen Sie zunächst einen beliebige Kundendaten-Satz in der Datei ab. Lesen Sie geschriebenen Daten wieder ein und geben Sie diese zur Kontrolle aus. Anschließend erhöhen Sie den Umsatz um 5 (Tsd-EUR) und überschreiben den alten Dateiinhalt nun mit den neuen Daten. Geben Sie zum Schluss die ein der Datei abgelegten Daten zur Kontrolle nochmals aus.
|
Kundendaten: |