Teil 1: Grundlagen


Dateizugriffe

Auch der Zugriff auf Dateien erfolgt, genauso wie die Ein- und Ausgabe, mittels Streams. Damit die Datei-Streams dem Compiler bekannt sind, ist die Header-Datei fstream mittels einer #include-Anweisung einzubinden.

Text- und Binardatei

Beim Bearbeiten von Dateien ist zwischen zwei Dateitypen zu unterscheiden: die Textdatei und die Binärdatei.

Eine Textdatei enthält nur ASCII-Zeichen. Diese Datei kann mit jedem einfachen Editor bearbeitet werden und numerische Daten sind in dieser Dateien ebenfalls als Zeichenfolge abgelegt. Die Textdatei ist die einzige Möglichkeit, Daten per Datei zwischen unterschiedlichen Systemen auszutauschen.

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. Der Aufbau einer Binärdatei ist nicht standardisiert, d.h., unterschiedliche Systeme können Daten unterschiedlich ablegen.

Daten in Datei ablegen

Ausgabestream-Objekt definieren

Damit Daten in eine Datei geschrieben werden können, ist zunächst ein Ausgabestream-Objekt wie folgt zu definieren:

std::ofstream strName;

ofstream ist die Klasse, die für die Datenausgabe verwendet wird und strName 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 strName mit dem Datentyp ofstream vor.

Ausgabestream-Objekt mit Datei verbinden

Um das Streamobjekt mit einer Datei zu verbinden, ist die Memberfunktion open(...) aufzurufen:

void ofstream::open(const char *pFName, ios::openmode mode=ios::out);

Der erste Parameter pFName ist ein Zeiger auf den Dateinamen und mode enthält den Modus, mit 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:

mode
Bedeutung
ios::out
Öffnen einer Datei zum Schreiben (default).
ios::trunc
Ö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 aktuellen Position eingefügt.
ios::app
Öffnen einer Datei und positionieren des Schreibzeigers auf das Dateiende. Die neuen Daten werden immer am Ende der Datei eingefügt, unabhängig davon ob der Schreibzeiger inzwischen neu positioniert wurde.
ios::binary
Öffnen einer Datei im Binärmodus. Ohne diese Angabe wird die Datei als Textdatei interpretiert.

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.

ofstream myFile(const char* pFName, ios::openmode mode = ios::out);

Da nicht auszuschließen ist, dass beim Öffnen einer Datei auch 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.

Schreiben in Textdatei

Das Schreiben in eine Textdatei erfolgt prinzipiell gleich wie die Ausgabe auf die Standardausgabe. Anstelle des Stream-Objekts cout wird nun lediglich der Name des Dateistream-Objekts angegeben. Dabei ist aber zu beachten, dass zwischen den zu schreibenden Daten mindestens ein Trennzeichen (Leerzeichen, Zeilenvorschub o.ä.) stehen muss.

Schreiben in Binärdatei

Das Schreiben eines einzelnen Bytes in eine Binärdatei erfolgt mit der Memberfunktion

ostream& put(char data);

wobei data das zu schreibende Byte ist.

Um Daten, die aus mehreren Bytes bestehen, in einer binären Datei abzulegen, wird die Memberfunktion

ostream& write(const char *pBuffer, streamsize 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.

Datei-Verbindung aufheben

Nachdem die Daten in der Datei abgelegt sind, muss die Verbindung des Streams zur Datei mittels der Memberfunktion close() aufgehoben werden.

void ofstream::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 <fstream> // Fuer Ein-/Ausgabe in Datei
using std::cout;
using std::endl;

int main()
{
    // Pfad fuer Textdatei
    const char *const pTextFile = "c:\\temp\\text.dat";
    // Pfad fuer Binaerdatei
    const char *const pBinFile = "c:\\temp\\bin.dat";
    // Zwei beliebige Daten zum Schreiben
    char letter = 'a';
    short var = 0x1122;

    // Textdatei oeffnen
    // -----------------
    std::ofstream file;
    file.open(pTextFile);
    // Pruefen ob Datei geoeffnet werden konnte
    if (!file)
    {
        cout << "Fehler beim Oeffnen von " << pTextFile << endl;
        exit(1);
    }
    // Daten in Textdatei ablegen
    // Trenner zwischen den Daten nicht vergessen!
    file << letter << ' ' << var << endl;
    file << "Ende der Datei";
    // Textdatei wieder schliessen
    file.close();

    // Binaerdatei oeffnen
    // -------------------
    file.open(pBinFile, std::ios::out | std::ios::binary);
    // Pruefen ob Datei geoeffnet werden konnte
    if (!file)
    {
        cout << "Fehler beim Oeffnen von " << pBinFile << endl;
        exit(2);
    }
    // Einzelnes Byte schreiben
    file.put(letter);
    // short Variable (2 Bytes) schreiben
    file.write(reinterpret_cast<const char*>(&var), sizeof(var));
    file.write("Ende der Datei", 14);
    // Binaerdatei wieder schliesen
    file.close();
    cout << "Daten in Datei " << pTextFile << " und " << pBinFile << " geschrieben!" << endl;
}

Daten aus Datei einlesen

Eingabestream-Objekt definieren

Ein Eingabestream-Objekt wird mit folgender Anweisung definiert:

ifstream strName;

ifstream ist wiederum die Klasse, die für die Dateneingabe verwendet wird und strName der Name des Stream-Objekts.

Eingabestream-Objekt mit Datei verbinden

Nachdem das Stream-Objekt erstellt wurde, kann es mit einer Datei verbunden werden. Dies erfolgt ebenfalls durch den Aufruf der Memberfunktion

void ifstream::open(const char *pFName, ios::openmode mode=ios::in);

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:

Mode
Bedeutung
ios::in
Öffnen einer Datei zum Lesen.
ios::binary
Öffnen einer Datei im Binärmodus. Ohne diese Angabe wird die Datei als Textdatei interpretiert.

Beim Aufruf der Memberfunktion kann auch 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.

ifstream myFile(const char* pFName, ios::openmode mode = ios::in);

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.

Lesen aus Textdatei

Das Lesen aus einer Textdatei erfolgt auch hier 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.

Lesen aus Binärdatei

Das Lesen eines einzelnen Bytes erfolgt mit der Memberfunktion

istream& get(char& data);

Das ausgelesene 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 get(...) erhält eine Referenz auf die Variable, in der das eingelesene BYte abzulegen ist. In ihrem Programm übergeben Sie die einzulesende Variable einfach 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 ist hierfür die Memberfunktion

istream& read(char *pBuffer, streamsize 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.

Datei-Verbindung mit Eingabestream-Objekt aufheben

Die Dateiverbindung wird auch hier mit der Memberfunktion

void ifstream::close();

wieder aufgehoben.

#include <iostream>
#include <fstream> // Fuer Ein-/Ausgabe in Datei

using std::cout;
using std::endl;

int main()
{
    // Pfad fuer Textdatei
    const char *const pTextFile = "c:\\temp\\text.dat";
    // Pfad fuer Binaerdatei
    const char *const pBinFile = "c:\\temp\\bin.dat";
    // Zwei beliebige Daten zum Einlesen
    char letter;
    short var;
    // char-Feld zum Einlesen eines Strings
    const int SIZE = 80;
    char text[SIZE];

    // Textdatei oeffnen
    // -----------------
    std::ifstream file;
    file.open(pTextFile);
    // Pruefen ob Datei geoeffnet werden konnte
    if (!file)
    {
        cout << "Fehler beim Oeffnen von " << pTextFile << endl;
        exit(1);
    }
    // Daten aus Textdatei auslesen
    file >> letter >> var;
    // Rest der Zeile in der Textdatei ueberspringen
    file.ignore();
    // Text einlesen
    file.getline(text, SIZE);
    // Textdatei wieder schliessen
    file.close();
    // Eingelesene Daten ausgeben
    cout << "letter: " << letter << ", var: "
         << var << "\nText: " << text << endl;

    // Variablen mit beliebigen Daten ueberschreiben
    letter = ' ';
    var = 0;
    text[0] = 0;

    // Binaerdatei oeffnen
    // -------------------
    file.open(pBinFile, std::ios::in | std::ios::binary);
    // Pruefen ob Datei geoeffnet werden konnte
    if (!file)
    {
        cout << "Fehler beim Oeffnen von " << pBinFile << endl;
        exit(2);
    }
    // Einzelnes Byte lesen
    file.get(letter);
    // short Variable (2 Bytes) lesen
    file.read(reinterpret_cast<char*>(&var), sizeof(var));
    // Text mit 14 Zeichen einlesen
    file.read(text,14);
    // Binaerdatei wieder schliesen
    file.close();
    // Eingelesene Daten ausgeben
    cout << "letter: " << letter << ", var: "
         << var << "\nText: " << text << endl;
}

Sonstige Dateioperationen

eof() Memberfunktion

In der Praxis wird in den meisten Fällen nicht von vornherein bekannt sein, wie viele Daten in einer Datei abgelegt sind. 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 wurde. Damit eof() das Erreichen des Dateiendes feststellen kann, muss vorher ein Leseversuch stattgefunden haben.

ignore() und seekX() Memberfunktion

Mit der Memberfunktion ignore() können Daten beim Einlesen übersprungen werden. Wird ignore() ohne Parameter (also mit einer leeren Klammer) aufgerufen und die einzulesende Datei ist im Textmodus geöffnet, so werden alle Eingaben ab der aktuellen Position bis einschließlich des Zeilenendes übersprungen. Beim Einlesen von Binärdateien erhält die Funktion in der Klammer die Anzahl der zu überspringenden Bytes.

Außerdem gibt es noch die Memberfunktion seekg(...) und seekp(...) um die internen Dateizeiger zu verschieben. Mehr dazu im Buch oder wieder unter https://en.cppreference.com.



Copyright © cpp-tutor.de
Impressum &Datenschutz