Dateizugriffe

Vorneweg gleich ein dickes ACHTUNG!

Enthält ein Dateiname eine Pfadangabe, sollte als Trennzeichen für Verzeichnisnamen anstelle eines Backslashs \ ein Schrägstrich /  verwendet werden. Der Grund hierfür ist, dass innerhalb eines Strings das Backslash-Zeichen eine Escape-Sequenz einleitet. Daher wird die Angabe c:\temp als c:[TAB]emp interpretiert. Der Schrägstrich als Trennzeichen ist auch unter WINDOWS gültig.

Text- und Binärdatei

Bevor auf die Bearbeitung von Dateien eingegangen werden kann, sind die Begriffe zwei Begriff zu klären: was ist eine Textdatei und was eine Binärdatei. Die Unterscheidung ist notwendig, da die Dateitypen unterschiedlich gehandhabt werden.

Textdatei

Textdateien sind Dateien die nur ASCII-Zeichen enthalten. Diese Dateien können mit jedem Texteditor bearbeitet werden. Numerische Daten sind in diesen Dateien ebenfalls als ASCII-Sequenz abgelegt.

Beispiel:

Eine Datei enthält den Text "Emil Maier", den short-Wert 100 und den long-Wert 0x11223344.

Dateiinhalt:

ASCII-Darstellung Hex-Darstellung
Emil Maier 45 6D 69 6C 20 4D 61 69 65 72
100 0x31 0x30 0x30
287454020 0x32 0x38 0x37 0x34 0x35 0x34 0x30 0x32 0x30

Diese Dateiform ist die einzige Möglichkeit, Dateien zwischen unterschiedlichen Plattformen auszutauschen, da das Dateiformat der nachfolgenden Binärdatei nicht standardisiert ist.

Binärdatei

In Binärdateien sind die Daten in binärer Form abgelegt, also in der Form, wie sie im Speicher des Rechners liegen. Diese Daten können in der Regel nicht mit einem Texteditor bearbeitet werden, da diese nur Dateien mit ASCII-Zeichen sinnvoll darstellen können. Der Aufbau einer Binärdatei ist, wie erwähnt, nicht standardisiert, d.h., unterschiedliche Systeme können Daten unterschiedlich ablegen.

Die Ablage eines Textes in einer Binärdatei unterscheidet sich nicht von der in einer Textdatei. Der Unterschied wird erst bei der Ablage von numerischen Daten deutlich.

Beispiel:

Die Datei enthält wieder den Text "Emil Maier", den short-Wert 100 und den long-Wert 0x11223344.

ASCII-Darstellung Hex-Darstellung
Emil Maier 45 6D 69 6C 20 4D 61 69 65 72
d. 0x64 0x00
D3". 0x44 0x33 0x22 0x11

Sind viele numerische Daten in einer Datei abzulegen und der Datenaustausch mit anderen Systemen ist nicht relevant, sollten die Daten in einer Binärdatei abgelegt werden. Dies ist zum einen platzsparender und zum anderen werden die Daten schneller eingelesen, da beim Schreiben und Lesen der Daten keine Konvertierung von ASCII nach Binär notwendig ist.

Anwenden von Dateistreams

Für die Bearbeitung von Dateien werden ebenfalls Streams eingesetzt. Diese Dateistreams sind in der Header-Datei fstream definiert und liegen ebenfalls im Namensraum std.

Um mithilfe der Streams Dateien zu bearbeiten, sind folgende Schritte notwendig:

Datei beschreiben

Ausgabestream-Objekt definieren

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

std::ofstream myFile;

ofstream ist der Stream, der für die Ausgabe in eine Datei verwendet wird, und myFile ein beliebiger Name des Streamobjekts.

Ausgabestream-Objekt mit Datei verbinden

Standardmäßig besitzt das Streamobjekt keine Verbindung zu einer Datei. Diese Verbindung erfolgt erst durch den Aufruf der ofstream-Methode

void open(const char *pFName[, OMODE mode]);

pFName ist ein Zeiger auf den Namen der Datei (oder später auch ein string-Objekt), die mit dem Stream verbunden werden soll.

Der optionale zweite Parameter mode spezifiziert den Modus, in dem die Datei geöffnet werden soll. Er kann eine sinnvolle Kombination durch Veroderung aus folgenden Werten sein:

OMODE Bedeutung
std::ios::out Öffnen einer Datei zum Schreiben (default).
std::ios::trunc Öffnen einer Datei, wobei der ursprüngliche Inhalt verworfen wird (default).
std::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 an der aktuellen Position eingefügt.
std::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.
std::ios::binary Öffnen einer Datei im Binärmodus.

Wird beim Aufruf von open() der zweite Parameter nicht angegeben, wird die Datei für die Ausgabe im Textmodus geöffnet und der ursprüngliche Inhalt verworfen. Wird der Modus explizit vorgegeben, ist immer der Modus std::ios::out mit anzugeben!

Es gibt eine zweite Form, ein Ausgabestream-Objekt zu definieren und dabei gleichzeitig mit einer Datei zu verbinden.

std::ofstream myFile(const char* pFName[,OMODE mode]);

Hier wird bei der Definition des Streamobjekts die zu öffnende Datei angegeben, gefolgt vom optionalen Modus.

1. Form

#include <fstream>

int main ()
{
   // Streamobjekt definieren
   std::ofstream myFile;
   // und anschliessend mit Textdatei verbinden
   myFile.open ("d:\\tmp\\test.dat");
   // Datei bearbeiten und schliessen
}

2. Form

#include <fstream>

int main ()
{
   // Streamobjekt definieren und  mit
   // Binaerdatei verbinden
   std::ofstream myFile ("d:/tmp/test.dat",
                    std::ios::out|std::ios::binary);
   // Datei beiarbeiten und schliessen
}

Im ersten Fall wird zunächst das Streamobjekt definiert und anschließend mittels open() mit einer Datei verbunden. Bitte beachten Sie, dass vor dem Namen der Methode open() der Name des Streamobjekts steht und danach ein Punkt folgt. Dies ist die allgemeine Syntax für den Aufruf der Methode eines Objekts.

Da nicht ausgeschlossen werden kann, dass beim Öffnen einer Datei einmal etwas schief geht, sollte nach der Verbindung des Streams mit einer Datei stets eine Fehlerabfrage erfolgen. Wie ein solcher Fehler abgefangen wird, ist nachfolgende dargestellt.

#include <fstream>
#include <print>

std::string FNAME {"d:/temp/test.dat"};

int main ()
{
   // Streamobjekt definieren
   std::ofstream myFile;
   // Datei oeffnen und Fehler abfangen
   myFile.open(FNAME);
   if (!myFile)
   {
      std::println("{} kann nicht geoeffnet werden!",FNAME);
      exit(1);  // z.B. Programm beenden
   }
}

Schreiben in Datei

Textdatei

Das Schreiben in eine Textdatei erfolgt prinzipiell gleich wie die Ausgabe auf die Standardausgabe mittels std::cout. Leider ist im C++-Standard keine print()-Funktion definiert, die als Ausgabeziel ein ofstream-Objekt zulässt. D.h., sollen Daten formatiert in eine Textdatei geschrieben werden, sind diese zuvor mit der Bibliotheksfunktion std::format() zu formatieren (siehe auch letzte Anweisung im nachfolgenden Beispiel).

Beim Schreiben ist zu beachten, dass zwischen den zu schreibenden Daten mindestens ein Trennzeichen (Leerzeichen, Zeilenvorschub o.ä.) steht, da die Daten ansonsten später nicht mehr korrekt eingelesen werden können.

Im nachfolgenden Beispiel erzeugen alle zwei Schreiboperationen die gleiche Ausgabe. Die Fehlerbehandlung beim Öffnen der Datei wurde der Übersichtlichkeit wegen weggelassen.

#include <fstream>
#include <format>
#include <print>

int main()
{
   // Beliebiges Datum definieren/initialisieren
   int var = 11;
   // Dateistream definieren und gleichzeitig
   // mit einer Datei verbinden
   std::ofstream myFile("c:/temp/xxx.txt");
   // Alle nachfolgenden Schreiboperationen erzeugen
   // die gleiche Ausgabe
   // 1. Direktes Schreiben
   myFile << "Ein Test: " << var << '\n';
   // 2. Schreiben mittels format()
   myFile << std::format("Ein Test: {}\n",var);
   // Weitere Schreiboperationen und Datei schliessen
}

Binärdatei

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

std::ostream& put(char data);

wobei data das zu schreibende Byte enthält.

Sollen Daten, die aus mehreren Bytes bestehen (short, long usw.), in einer binären Datei abgelegt werden, ist hierfür die Methode

std::ostream& write(const char *pBuffer,
                    streamsize bytes);

einzusetzen. pBuffer ist ein Zeiger auf den Beginn des zu schreibenden Datenblocks und bytes gibt die Anzahl der zu schreibenden Bytes an. Da die Methode einen const char-Zeiger erwartet, ist in der Regel eine Typkonvertierung vorzunehmen.

#include <fstream>

// Variablen definieren
short var1 = 10;
long  var2 = 20;

int main ()
{
   // Streamobjekt definieren und mit Datei verbinden
   std::ofstream myFile;
   myFile.open ("d:/tmp/test.dat",
                std::ios::out|std::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
   }
}

Wenn Sie sich das Beispiel oben genauer ansehen, werden Sie bemerken, dass dort nur eine Typkonvertierung nach char* erfolgt und nicht nach const char*. Ein char* kann immer anstelle eines const char* übergeben werden, aber niemals umgekehrt! Das const char* bei der Signatur der Methode write() sagt nur aus, dass die Methode den Inhalt des übergebenen Puffers nicht verändert.

Beim Schreiben von Daten in Binärdateien können die Daten unmittelbar hintereinandergeschrieben werden, es sind keine Trennzeichen wie bei einer Textdatei notwendig, um später die Daten wieder einlesen zu können.

Dateiverbindung aufheben

Nachdem die Daten in die Datei übertragen wurden, ist die Verbindung des Streams zur Datei wieder aufzuheben. Hierbei gibt es wiederum zwei Möglichkeiten.

Im ersten Fall wird die Verbindung zur Datei durch den Aufruf der Methode

void close();

aufgehoben. Das Streamobjekt besteht danach weiter und kann durch erneuten Aufruf von open() mit einer weiteren Datei verbunden werden.

#include <fstream>

int main ()
{
   // Streamobjekt definieren und mit Datei verbinden
   std::ofstream myFile;
   myFile.open ("c:/tmp/test.dat");
   // ... Datei bearbeiten
   // Dateiverbindung aufheben
   myFile.close();
   // Stream erneut mit einer Datei verbinden
   myFile.open("c:/tmp/anotherfile.dat");
   // ... Datei bearbeiten
   // Datei schließen
   myFile.close();
}

Die zweite Möglichkeit, eine Verbindung zur Datei aufzuheben, besteht darin, das Streamobjekt zu löschen. Wenn ein Streamobjekt, wie im nachfolgenden Beispiel dargestellt, innerhalb eines Blocks {...} definiert ist, wird es am Blockende automatisch gelöscht und damit dessen Verbindung zur Datei aufgehoben. Mehr zu Blöcken innerhalb eines Programms und der Gültigkeit von Variablen und Objekt später im Kapitel Lebensdauer und Sichtbarkeit von Daten.

#include <fstream>

int main ()
{
   // Blockbegin
   {
      std::ofstream myFile("d:/tmp/test.dat");
      // ... Datei bearbeiten
   }  // Blockende, hier wird das Streamobjekt geloescht!
}

Datei einlesen

Eingabestream-Objekt definieren

Ein Eingabestream-Objekt wird mit folgender Anweisung definiert:

std::ifstream myFile;

ifstream ist der Stream für das Einlesen von Daten aus einer Datei und myFile der Name des Streamobjekts. Das so definierte Streamobjekt myFile besitzt ebenfalls noch keine Verbindung zu einer Datei.

Eingabestream-Objekt mit Datei verbinden

Nachdem das Streamobjekt erstellt wurde, kann es mit einer Datei verbunden werden. Dies erfolgt wiederum durch den Aufruf der Methode

void open(const char *pFName[, OMODE mode]);

Der Parameter pFName ist ein Stringzeiger auf den Namen der zu öffnenden Datei (oder ein string-Objekt) und der zweite optionale Parameter mode gibt an, in welchem Modus die Datei zu öffnen ist. Er ist eine sinnvolle Kombination aus folgenden Werten:

OMODE Bedeutung
std::ios::in Öffnen einer Datei zum Lesen (default)
std::ios::binary Öffnen einer Datei im Binärmodus.

Wird beim Aufruf der Methode der zweite Parameter weggelassen, wird die Datei im Textmodus geöffnet.

Und auch hier besteht die Möglichkeit, ein Eingabestream-Objekt gleich bei seiner Definition mit einer Datei zu verbinden.

std::ifstream myFile(const char* pFName[, OMODE mode]);

1. Form

#include <fstream>
#include <print>

int main ()
{
   // Streamobjekt definieren
   std::ifstream myFile;
   // Datei oeffnen und Fehlerabfrage!
   myFile.open ("d:/tmp/test.dat");
   if (!myFile)
   {
      std::println("Fehler!");
      exit(1);  // Programm beenden
   }
}

2. Form

#include <fstream>
#include <print>

int main ()
{
   // Streamobjekt definieren und mit Datei verbinden
   std::ifstream myFile("d:/tmp/test.dat",
                        std::ios::in|std::ios::binary);
   // Fehlerabfrage!
   if (!myFile)
   {
      std::println("Fehler!");
      exit(1);   // Programm beenden
   }
}

Und auch beim Öffnen einer Datei zum Lesen können Fehler auftreten. Prüfen Sie deshalb immer, ob der Stream erfolgreich mit der Datei verbunden werden konnte.

Lesen aus Datei

Textdatei

Das Lesen aus einer Textdatei erfolgt prinzipiell gleich wie das Einlesen von der Standardeingabe mittels cin. Anstelle des Streamobjekts cin steht hier der Name des Eingabestream-Objekts. Alle zu lesenden Daten werden ebenfalls mit dem Operator >> aus dem Stream ausgelesen.

#include <fstream>

// Variablen definieren
int var1, var2;

int main ()
{
   // Streamobjekt definieren und mit Datei verbinden
   std::ifstream myFile;
   myFile.open ("d:/tmp/test.dat");
   // ... Hier Fehler abfangen!
   // Daten lesen
   myFile >> var1 >> var2;
   // ... Datei schließen
}

Da sowohl cin wie auch ifstream die gleiche Basisklasse basic_istream besitzen, stehen für das Einlesen von Daten aus einer Datei dieselben Manipulatoren, wie z.B. std::hex oder std::oct, zur Verfügung.

Wie eine ganze Zeile eingelesen werden kann wird im Kapitel String I beschrieben.

Binärdatei

Das Lesen eines Bytes erfolgt mit der Methode

std::istream& get(char& data);

Das ausgelesene Byte ist nach der Ausführung der Methode 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 die das eingelesene Byte abzulegen ist. Im Programm übergeben Sie einfach die einzulesende Variable an die Methode, also z.B.

myFile.get(charVar);

Sollen Daten, die aus mehreren Bytes bestehen (short, long usw.), aus einer Binärdatei eingelesen werden, ist hierfür die Methode

std::istream& read(char *pBuffer, streamsize bytes);

einzusetzen. pBuffer ist ein char-Zeiger auf den Beginn des Datenblocks, in dem die ausgelesenen Bytes abgelegt werden und bytes gibt die Anzahl der zu lesenden Bytes an. Da die Methode einen char-Zeiger erwartet, ist in der Regel ebenfalls eine Typkonvertierung erforderlich.

#include <fstream>
#include <print>

// Variablen definieren
int  var1;
long var2;

int main ()
{
   // Streamobjekt definieren und mit Datei verbinden
   std::ifstream myFile;
   myFile.open ("d:/tmp/test.dat",
                 std::ios::in|std::ios::binary);
   // Fehler abfangen!
   if (!myFile)
   {
      std::println("Fehler!");
      exit(1);
   }
   // Daten lesen
   myFile.read(reinterpret_cast<char*>(&var1),
               sizeof var1);
   myFile.read(reinterpret_cast<char*>(&var2),
               sizeof var2);
   // ... Datei schließen
}

Dateiverbindung mit Eingabestream-Objekt aufheben

Das Aufheben der Verbindung mit der Datei und das Löschen des Streamobjekts erfolgt auf die gleiche Weise wie beim Schreiben in eine Datei.

'Gleichzeitiges' Schreiben und Lesen

Und auch dies ist möglich: Eine Datei kann zum Schreiben und Lesen geöffnet werden. Hierzu wird ein Streamobjekt vom Typ fstream eingesetzt, das wie folgt definiert wird:

std::fstream myFile;

fstream ist der Stream für das Schreiben und Lesen von Daten aus einer Datei und myFile wieder der Name des Streamobjekts. Auch dieses Streamobjekt besitzt ebenfalls noch keine Verbindung zu einer Datei. Um das Streamobjekt mit einer Datei zu verbinden, wird entweder die Methode open() verwendet oder bei der Definition des Streamobjekts wird der Dateiname mit angegeben (genauso wie ifstream bzw. ofstream). Lediglich für den Parameter mode sind hier andere Kombinationen zugelassen:

OMODE Beduetung
std::ios:out Öffnen einer Datei zum Schreiben.
std::ios:in Öffnen einer Datei zum Lesen.
std::ios::binary Öffnen einer Datei im Binärmodus. Ohne diese Angabe wird die Datei als Textdatei interpretiert.
std::ios:app Öffnen einer Datei und positionieren des Schreibzeigers auf das Dateiende. Neue Daten werden immer ans Ende der Datei eingefügt.
std::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.
std::ios::trunc Öffnen einer Datei, wobei der ursprüngliche Inhalt verworfen wird.

Wird eine Datei zum Lesen und Schreiben geöffnet werden, ist der mode-Parameter immer mit anzugeben.

Soll eine neue Datei zum Schreiben und Lesen geöffnet werden, ist der Modus ios::trunc mit anzugeben. Ansonsten wird standardmäßig versucht, eine bestehende Datei zu öffnen.

Das Lesen und Schreiben der Daten sowie das Schließen der Datei erfolgt mit den in den vorherigen Abschnitten beschriebenen Methoden.

Alle 3 Streams (ofstream, ifstream und fstream) besitzen noch einen 3. Parameter, der den gleichzeitigen Zugriff auf eine Datei von mehreren Streams aus kontrolliert. Dieser Parameter ist aber nur für fortgeschrittene Dateioperationen interessant und wird hier nicht weiter betrachtet.

Sonstige Dateioperationen

eof() und clear() Methode

In der Praxis wird im Voraus nicht bekannt sein, wie viele Daten in einer Datei abgelegt sind. Die Abfrage, ob beim Einlesen das Dateiende erreicht ist, erfolgt mit der Methode

bool eof();

eof() liefert true zurück, wenn das Dateiende erreicht wurde. Damit eof() das Erreichen des Dateiendes feststellen kann, muss vorher ein Leseversuch stattgefunden haben.

Nach Erreichen des Dateiendes befindet sich der Dateistream in einem Fehlerzustand. Sollen danach weiter Schreib-/Leseoperationen erfolgen, ist die Methode

void clear();

aufzurufen, um den Fehlerzustand aufzuheben.

#include <fstream>

int main ()
{
    int data;

    // Streamobjekt definieren und mit Datei verbinden
    std::ifstream myFile("d:/test.dat");
    // Einlesen aus Datei
    myFile >> data;
    // Lesen bis Dateiende erreicht
    while (!myFile.eof())
    {
        // Weiter Daten aus Datei auswerten
    }
    // Dateistatus zuruecksetzen
    myFile.clear();
    // Hier koennte myFile jetzt weiter verwendet werden
}

ignore() Methode

Die Methode ignore() wird zum Überspringen von Daten in einer Datei verwendet.

basic_istream& ignore(std::streamsize count=1,
                       int_type delim=Traits::eof());

count definiert die Anzahl der zu überspringenden Daten und delim das Zeichen, bis zu dem die Daten tatsächlich übersprungen werden. Standardmäßig wird ein Datum übersprungen aber bis maximal das Dateiende erreicht wurde.

#include <fstream>
#include <print>

int main()
{
   // Feld zum Einlesen eines Strings
   char array[100];

   // Streamobjekt definieren und mit Datei verbinden
   std::ifstream infile("c:/temp/test.dat");
   // Wenn Datei geöffnet werden konnte
   if (infile)
   {
      // Erstes Wort einlesen
      infile >> array;
      std::println("1. Wort: {}", array);
      // Rest der 1. Zeile
      // (aber max. 100 Zeichen) überspringen
      infile.ignore(100,'\n');
      // Erstes Wort der 2. Zeile einlesen
      infile >> array;
      std::println("2. Wort: {}", array);
   }
   else
      std::println("Fehler beim Oeffnen der Datei!");
}

seek() Methoden

Bei Dateistreams lassen sich die internen Dateizeiger (genau genommen sind es Pufferzeiger) beeinflussen, um einen wahlfreien Zugriff auf die in der Datei abgelegten Daten zu erhalten. Hierzu werden die beiden Methoden

std::basic_istream& seekg (ios:off_type offset,
                           DIR pdir);
std::basic_ostream& seekp (ios::off_type offset,
                           DIR pdir);

verwendet. Die Methode seekg() dient zum Positionieren des Lesezeigers und die Methode seekp() zum Positionieren des Schreibzeigers. Beide Zeiger lassen sich unabhängig voneinander positionieren. offset gibt die Anzahl der Bytes an, um die der Dateizeiger von der Position pdir aus bewegt werden soll. Für pdir ist einer der folgenden Werte zulässig:

DIR Bedeutung
std::ios::cur Dateizeiger ab der aktuellen Position verschieben.
std::ios::beg Dateizeiger ab Dateianfang verschieben.
std::ios::end Dateizeiger ab Dateiende verschieben.

Dabei ist zu beachten, dass der Dateizeiger bei Angabe von std::ios::beg nur in positiver Richtung und bei Angabe von std::ios::end nur in negativer Richtung verschoben werden kann!

Im nachfolgenden Beispiel werden zunächst die Werte 1...5 in eine Binärdatei geschrieben, die zum Lesen und Schreiben geöffnet ist. Anschließend werden mit Hilfe der erwähnten seek-Methoden verschiedene Werte aus der Datei ausgelesen. Beachten Sie die Kommentare im Listing. Zum Schluss wird der vorletzte Wert verändert und dann alle Daten zur Kontrolle nochmals ausgegeben.

// Beispiel zu wahlfreiem Dateizugriff
#include <fstream>
#include <print>

// Dateiname
auto const FILENAME = "test.dat";

int main()
{
    // Streamobjekt mit binärer Datei verbinden
    std::fstream inOutFile(FILENAME, std::ios::binary |
           std::ios::in | std::ios::out | std::ios::trunc);
    // Fehler abfangen
    if (!inOutFile)
    {
        std::println("Fehler beim Oeffnen der Datei {}",
                      FILENAME);
        exit(1);
    }

    // Werte 1...5 in Datei schreiben
    int var;
    for (var=1; var<6; var++)
        inOutFile.write(reinterpret_cast<char*>(&var),
                        sizeof var);

    // Auf Dateianfang zurück
    inOutFile.seekg(0, std::ios::beg);
    // 1. Datum einlesen
    inOutFile.read(reinterpret_cast<char*>(&var),
                   sizeof var);
    std::println("1. Wert: {}", var);
    // Auf letzten Wert positionieren
    inOutFile.seekg(-sizeof var, std::ios::end);
    inOutFile.read(reinterpret_cast<char*>(&var),
                   sizeof var);
    std::println("Letzter Wert: {}", var);
    // Auf vorletzten Wert positionieren
    inOutFile.seekg(sizeof var * -2, std::ios::end);
    inOutFile.read(reinterpret_cast < char*>(&var),
                   sizeof var);
    std::println("Vorletzter Wert: {}\n", var);
    // Vorletzten Wert überschreiben
    std::println("Ueberschreibe jetzt vorletzten Wert!");
    var = -1;
    inOutFile.seekp(sizeof var * -2, std::ios::end);
    inOutFile.write(reinterpret_cast<char*>(&var),
                    sizeof var);
    // Und nun alle Werte auslesen
    std::println("Datei enthaelt jetzt:");
    // Lesezeiger zuerst auf Dateianfang
    inOutFile.seekg(0, std::ios::beg);
    // Nun alles wieder einlesen bis zum Dateiende
    do
    {
        inOutFile.read(reinterpret_cast<char*>(&var),
                       sizeof var);
        // Wenn nicht Dateiende erreicht, Wert ausgeben
        if (!inOutFile.eof())
            std::print("{} ",var);
    } while (!inOutFile.eof());
}

1. Wert: 1
Letzter Wert: 5
Vorletzter Wert: 4

Ueberschreibe jetzt vorletzten Wert!
Datei enthaelt jetzt: 1 2 3 -1 5

Übungen

Sie finden die nachfolgend aufgeführten Dateien im Begleitmaterial zum Tutorial, das Sie unter https://www.cpp-tutor.de/download/Begleitmaterial_cpp23.zip runterladen können.

file_01

Die Datei preisindex.txt enthält die Entwicklung der Preise in Deutschland ab 2020.

2020 100.0
2021 103.1
2022 110.2
2023 116.7
2024 118.7

Lesen Sie alle Einträge der Datei ein und geben sie wie unten angegeben aus.

Fügen Sie einen geschätzten Preisindex von 121.5 für das Jahr 2025 hinzu.

Geben Sie alle Einträge erneut aus.

Preisindizes:
Preisindex 2020: 100.00
Preisindex 2021: 103.10
Preisindex 2022: 110.20
Preisindex 2023: 116.70
Preisindex 2024: 118.70

Aktualisierte Preisindizes:
Preisindex 2020: 100.00
Preisindex 2021: 103.10
Preisindex 2022: 110.20
Preisindex 2023: 116.70
Preisindex 2024: 118.70
Preisindex 2025: 121.50

file_02

Die Datei messw.dat enthält die Daten einer Messwertreihe in binärer Form. Die Daten liegen als vorzeichenlose 16-Bit Werte in der Datei.

Es sind alle Daten einzulesen und auszugeben.

Geben Sie den kleinsten und größten Wert aus.

Eingelesene Messwerte: 41, 67, 34, 0, 69, 24, 78, 58, 62, 64,
Kleinster Wert: 0, groesster Wert: 78