Das Schreiben und Lesen von Daten in bzw. aus Dateien erfolgt i.d.R. mit den Streams ofstream bzw. ifstream. Damit der Compiler diese kennt, ist die Header-Datei <fstream> einzubinden.
Beim Bearbeiten von Dateien ist zwischen einer Textdatei und einer Binärdatei zu unterscheiden.
Eine Textdatei enthält nur ASCII-Zeichen. Diese Datei kann mit jedem Editor bearbeitet werden und numerische Daten sind ebenfalls als Text abgelegt.
Im Gegensatz dazu ist eine Binärdatei eine Datei, in denen Daten in binärer Form, also in der Form wie sie im Speicher des Rechners liegen, abgelegt sind.
Damit Daten in eine Datei geschrieben werden können, ist zunächst ein Ausgabestream-Objekt wie folgt zu definieren:
std::ofstream myFile;
ofstream ist die Klasse, die für die Datenausgabe verwendet wird und myFile ist der frei wählbare Name des Stream-Objekts. Klassen und Objekte werden zwar erst später behandelt, aber stellen Sie sich unter der obigen Anweisung im Prinzip die Definition einer Variablen myFile mit dem Datentyp ofstream vor.
Um das Stream-Objekt mit einer Datei zu verbinden, wird die Memberfunktion open() aufgerufen:
myFile.open(pFName, mode);
Der erste Parameter pFName ist ein char-Zeiger auf den Dateinamen und mode enthält den Modus, in dem 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:
Der zweite Parameter mode kann auch weggelassen werden. In diesem Fall wird die Datei für die Ausgabe im Textmodus geöffnet und der ursprüngliche Dateiinhalt wird verworfen.
Alternativ kann ein Ausgabestream gleich bei seiner Definition mit einer Datei verbunden werden.
std::ofstream myFile(pFName, mode);
Da nicht auszuschließen ist, dass beim Öffnen einer Datei einmal etwas schief geht, sollte nach der Verbindung des Streams mit einer Datei stets eine Fehlerabfrage erfolgen. Wie dieses Abfangen von Fehlern erfolgt, ist im nachfolgenden Beispiel dargestellt.
// Textdatei oeffnen
std::ofstream outFile("c:/temp/xxx.txt");
// Fehler abfangen
if(!outFile)
{
// Fehlerbehandlung durchfuehren
}
Sind die Daten in der Datei abgelegt (wird gleich beschrieben), muss die Verbindung des Streams zur Datei mittels der Memberfunktion close() aufgehoben werden.
myFile.close();
Das Stream-Objekt besteht danach weiterhin und es kann mittels open() eine weitere Datei mit dem gleichen Stream-Objekt verbunden werden.
#include <iostream>
#include <format>
#include <fstream>
int main()
{
// Datei im Binaermodus oeffnen
std::ofstream outFile("c:/temp/xxx.bin", std::ios::out | std::ios::binary);
// Fehler abfangen
if(!outFile)
{
std::cout << "Fehler beim Oeffnen der Datei!\n"; exit(1);
}
// Daten in Datei ablegen
// .....
// Datei schliessen
outFile.close();
// Weitere Datei oeffnen, jetzt im Textmodus
outFile.open("c:/temp/yyy.txt");
// Fehler abfangen
if(!outFile)
{
std::cout << "Fehler beim Oeffnen der Datei!\n";
exit(2);
}
// Daten in Datei ablegen
// ...
// Datei schliessen
outFile.close();
}
Das Schreiben in eine Textdatei erfolgt prinzipiell gleich wie die Ausgabe auf die Standardausgabe. Anstelle des Stream-Objekts cout wird nun jedoch der Name des Dateistream-Objekts angegeben. Dabei ist zu beachten, dass beim Schreiben der Daten zwischen diesen mindestens ein Trennzeichen (Leerzeichen, Zeilenvorschub o.ä.) steht, da sie ansonsten später nicht wieder richtig eingelesen werden können.
#include <iostream>
#include <format>
#include <fstream>
int main()
{
// Datei im Textmodus oeffnen
std::ofstream outFile("c:/temp/xxx.txt");
// Fehler abfangen
if(!outFile)
{
std::cout << "Fehler beim Oeffnen der Datei!\n";
exit(1);
}
// Daten in Datei ablegen
unsigned long ulvar = 0x12345678UL;
outFile << std::format("Datum: {}\n",ulvar);
// Datei schliessen
outFile.close();
}
Das Schreiben eines einzelnen Bytes in eine Binärdatei erfolgt mit der Memberfunktion put(...)
myFile.put(data);
wobei data das zu schreibende Byte ist.
Um Daten, die aus mehreren Bytes bestehen, in einer binären Datei abzulegen, wird die Memberfunktion
myFile.write(pBuffer, bytes);
eingesetzt. pBuffer ist ein const char-Zeiger auf den Beginn des Datenblocks und bytes gibt die Anzahl der zu schreibenden Bytes an. Da die Memberfunktion einen const char-Zeiger erwartet, muss in der Regel eine Typkonvertierung vorgenommen werden, wenn numerische Daten abgespeichert werden.
#include <iostream>
#include <format>
#include <fstream>
int main()
{
// Datei im Binaermodus oeffnen
std::ofstream outFile("c:/temp/xxx.bin",std::ios::out|std::ios::binary);
// Fehler abfangen
if(!outFile)
{
std::cout << "Fehler beim Oeffnen der Datei!\n";
exit(1);
}
// Daten in Datei ablegen
char letter = 'A';
const char *pText = "Datum: ";
unsigned long ulvar = 0x12345678UL;
outFile.put(letter);
outFile.write(pText,strlen(pText));
outFile.write(reinterpret_cast<const char*>(&ulvar),sizeof ulvar);
// Datei schliessen
outFile.close();
}
Ein Eingabestream-Objekt wird mit folgender Anweisung definiert:
std::ifstream myFile;
ifstream ist wiederum die Klasse, die für die Dateneingabe verwendet wird und myFile der Name des Stream-Objekts.
Nachdem das Stream-Objekt erstellt wurde, kann es mit einer Datei verbunden werden. Dies erfolgt ebenfalls durch den Aufruf der Memberfunktion
myFile.open(pFName, mode);
Der erste Parameter pFName ist ein Zeiger auf den Dateinamen und mode enthält den Modus, in dem die Datei geöffnet werden soll. Er kann eine sinnvolle Kombination aus folgenden Werten sein:
Beim Aufruf der Memberfunktion kann der zweite Parameter weggelassen werden. In diesem Fall wird die Datei im Textmodus geöffnet.
Auch hier besteht die Möglichkeit, einen Eingabestream gleich bei seiner Definition mit einer Datei zu verbinden.
std::ifstream myFile(pFName, mode);
Und auch beim Öffnen einer Datei zum Lesen können natürlich Fehler auftreten. Prüfen Sie deshalb immer ab, ob die Datei erfolgreich mit dem Stream verbunden werden konnte.
Die Dateiverbindung wird wiederum mit der Memberfunktion
myFile.close();
aufgehoben.
#include <iostream>
#include <format>
#include <fstream>
int main()
{
// Datei im Textmodus oeffnen
std::ifstream inFile("c:/temp/xxx.txt");
// Fehler abfangen
if(!inFile)
{
std::cout << "Fehler beim Oeffnen der Datei!\n";
exit(1);
}
// Daten aus Datei einlesen
// ....
// Datei schliessen
inFile.close();
}
Das Lesen aus einer Textdatei erfolgt prinzipiell gleich wie das Lesen von Daten von der Standard-Eingabe mittels cin. Anstelle des Streamobjekts cin steht hier nun der Name des Eingabestream-Objekts.
#include <iostream>
#include <format>
#include <fstream>
int main()
{
// Datei im Textmodus oeffnen
std::ifstream inFile("c:/temp/xxx.txt");
// Fehler abfangen
if(!inFile)
{
std::cout << "Fehler beim Oeffnen der Datei!\n";
exit(1);
}
// Daten aus Datei lesen
unsigned long ulvar;
char text[80];
inFile >> text >> ulvar;
// Und Daten ausgeben
std::cout << std::format("{} {}\n",text,ulvar);
// Datei schliessen
inFile.close();
}
Das Lesen eines einzelnen Bytes erfolgt mit der Memberfunktion
myFile.get(data);
Das ausgelesene Byte ist nach der Ausführung der Memberfunktion in der Variable data abgelegt.
Sollen Daten, die aus mehreren Bytes bestehen (short, long usw.), aus einer Binärdatei ausgelesen werden, so ist hierfür die Memberfunktion
myFile.read(pBuffer, bytes);
einzusetzen. pBuffer ist ein char-Zeiger auf den Beginn des Datenblocks, in dem die aus der Datei ausgelesenen Bytes abgelegt werden sollen und bytes gibt die Anzahl der zu lesenden Bytes an. Da die Memberfunktion wiederum einen char-Zeiger erwartet, muss in der Regel eine entsprechende Typkonvertierung erfolgen, wenn numerische Daten eingelesen werden.
#include <iostream>
#include <format>
#include <fstream>
int main()
{
// Datei im Binaermodus oeffnen
std::ifstream inFile("c:/temp/xxx.bin",std::ios::in|std::ios::binary);
// Fehler abfangen
if(!inFile)
{
std::cout << "Fehler beim Oeffnen der Datei!\n";
exit(1);
}
// Daten aus Datei auslesen
char letter;
char text[80];
unsigned long ulvar;
inFile.get(letter);
inFile.read(text,7);
inFile.read(reinterpret_cast<char*>(&ulvar),sizeof ulvar);
// Daten ausgeben
std::cout << std::format("{}{}{:#x}\n",letter,text,ulvar);
// Datei schliessen
inFile.close();
}
Die Abfrage, ob beim Lesen aus einer Datei das Dateiende erreicht wurde, erfolgt mit der Memberfunktion
myFile.eof();
Diese Memberfunktion liefert den Wert true zurück, wenn das Dateiende erreicht wurde. Damit eof() das Erreichen des Dateiendes feststellen kann, muss vorher ein Leseversuch stattgefunden haben.
// Schleife um alle Daten aus einer Datei einzulesen
do
{
// Datum einlesen
inFile >> data;
// Wenn Dateiende nicht erreicht
if (!inFile.eof())
std::cout << std::format("{}, ",data);
} while (!inFile.eof()); // Ende der Schleife
Mit der Memberfunktion ignore() können Daten beim Einlesen übersprungen werden.
myFile.ignore(count, delim);
count ist die Anzahl der zu überspringenden Zeichen und delim definiert, bis zu welchem Zeichen maximal der Sprung erfolgt.
// Ueberspringe 10 Zeichen, aber maximal bis zum nächsten Semikolon
infile.ignore(10,';')
Die Memberfunktion seekg() und seekp() dienen dazu, die internen Dateizeiger zu verschieben. Mehr dazu im Buch oder wieder unter https://en.cppreference.com.