In diesm Artikel werden Dateieingabe und -ausgabeoperation mit der C++-Stadnartbiblothek ausführlich vorgestellt.
Wenn man mit C++ programmiert wird man früher oder später nicht Drumherum kommen irgendwelche Dateien zu lesen oder zu beschreiben.
Dafür gibt es mittlerweile viele Möglichkeiten, ich werde hier nur die von der C++- Standardbibliothek (wird ab jetzt mit
CPPLIB abgekürzt) bereitgestellte Möglichkeit besprechen und nur dieser sollte auch verwendet werden, da diese Plattformunabhängig ist und wer damit perfekt umgehen lernt, wird bei Portierungen keine Probleme haben. Genauso sollte hier gewonnenes Wissen auf die äquivalenten C-Funktionen übertragen werden können.
Über diese Artikelreihe:
Optionale Parameter werden mit Eckigen Klammern [] versehen, also solche die man nicht zwingen Angeben muss.
Die wichtigen Themen werden hier nur kurz Angeschnitten und sollte gegeben falls über externe Quellen vertieft werden.
Eine Datei wird durch ihren Speicherort, ihren Namen und ihren Inhalt definiert.
Der Aufbau des Speicherorts, sowie der Name der Datei kann von Betriebssystem zu Betriebssystem variieren und sieht unter Windows z.B.: so aus:
C:\Programme\Max\Hallo.txt
-> Datenträger Buchstabenidentifikation
-> Ordnerstruktur, einzelne Unterordner werden durch ein Backslash \ getrennt (hingegen bei Linux einfach nur Slash /)
-> Dateiname
-> Datei(namens)Erweiterung, deutet oft auf den gespeicherten Inhalt hin
Der Inhalt hat eine Variable Länge und wird durch verschiedene Bytefolgen bestimmt:

In diesem Bild sieht man eine Datei der Länge 16, im linken Teil des Fensters werden die Hexadezimal-Werte der einzelnen Bytes dargestellt und rechts die dazugehörige ASCII-Zeichen.
Siehe dazu auch
Datei.
Ein Byte hat 8 Bit und ein Bit ist entweder eine 1 oder eine 0.
In C++ gibt es verschiedene Datentypen die durch verschiedene Anzahl an Bytes repräsentiert werden, um diese Anzahl zu erhalten gibt es die
sizeof Funktion.
Siehe dazu auch
Byte und
Bit.
Im Prinzip gibt es keine, jedoch wird in der Programmierung oft zwischen diesen zwei Dateien, besser gesagt Modi unterschieden.
Im Binärmodus werden die Daten wie sie sind geschrieben, also Roh.
Im Textmodus werden nach Möglichkeiten alle Daten bzw. die übergebenden Datentypen in ihre ASCII-Repräsentation umgewandelt, selbe gilt in umgekehrter Richtung beim einlesen.
Jedem Zeichen wie Buchstaben und Zahlen sind festgelegte Werte zugewiesen um in der Computerwelt damit einheitlich arbeiten zu können und damit die Darstellung sicherzustellen.
Siehe dazu auch
ASCII.
In der Mathematik gibt es verschiedene Zahlensysteme, das bekannteste wird wohl das Dezimalsystem sein.
Des Weiteren gibt es noch Binär, Oktadezimal, Hexadezimal und viele andere, dies sind jedoch die wichtigsten.
Siehe dazu auch
Dualsystem,
Oktalsystem und
Hexadezimalsystem.
In der CPPLIB werden die Dateioperationen in drei Objekte unterteilt und zwar in Ausgabeobjekte(
ofstream), Eingabeobjekte(
ifstream) und in Ein-/Ausgabeobjekte(
fstream), diese sollten nach ihrem Aufgabengebiet ausgewählt werden um einmal Ressourcen zu sparen und um logische Fehler zu vermeiden.
Des Weiteren unterscheidet man zwischen
Binärdateien und
Textdateien, für Anfänger ist wohl letzteres interessanter und alle genannten Objekte werden standardmäßig in diesem Modus geöffnet, dabei werden die Standard Typen automatisch in Zeichenketten umgewandelt.
Nun um die oben genannte Objekte verwenden zu können muss man zuerst den dazugehörigen CPPLIB-Header einbinden:
|
C/C++ Quellcode
|
1
2
|
#include <fstream>
using namespace std;
|
Wer an dieser Stelle schon den CPPLIB-Header
iostream eingebunden hat kann die Einbindung von fstream weglassen, da diese von iostream automatisch eingebunden wird.
Der Befehl "using namespace std" muss
nur einmal (pro Gültigkeitsbereich definiert) werden, er bewirkt dass man auf namespace-Objekte, wie die Streamklassen direkt zugreifen kann, wer weiteres wissen will kann sich über den
scope operator selbstständig erkundigen.
Die Funktionen der Objekte
ofstream,
ifstream und
fstream sind nahezu identisch bis auf die Tatsache das
ofstream-Objekte nur zur Dateieingabe, die
ifstream-Objekte nur zur Dateiausgabe und
fstream-Objekte für beides benutzt werden können und dementsprechend nur für das Objekt relevanten Funktionen besitzen.
Wird verwendet um ein Stream-Objekt zu erstellen und bei Bedarf um direkt eine Datei zu öffnen:
|
C/C++ Quellcode
|
1
2
3
|
fstream stream[ (const char * Dateiname [, openmode Mode = ios_base::in | ios_base::out]) ];
ifstream stream[ (const char * Dateiname [, openmode Mode = ios_base::in]) ];
ofstream stream[ (const char * Dateiname [, openmode Mode = ios_base::out]) ];
|
Parameter Dateiname: Muss ein C-String sein, also eine nullterminierte Zeichenkette sein.
Parameter Mode: Hat je nach Objekt verschiedene Standartwerte (siehe oben). Die Eigenschaften können mit einer
oder-Verknüpfung | logisch kombiniert werden. Mögliche Werte:
- ios_base::app (append) Setzt den Dateipositionszeiger vor jeder Ausgabe ans Ende der Datei.
- ios_base::ate (at end) Setzt den Dateipositionszeiger nach dem öffnen ans Ende der Datei.
- ios_base::binary (binary) Öffnet die Datei im Binär-Modus.
- ios_base::in (in) Öffnet die Datei im Lese-Modus (Eingabe).
- ios_base::out (out) Öffnet die Datei im Schreib-Modus (Ausgabe).
- ios_base::trunc (truncate) Der Inhalt der geöffneten Datei wird gelöscht.
Anmerkung:
Das
ostream-Objekt löscht mit den Standartparametern
immer den Inhalt der zu öffnenden Datei, obwohl ios_base::trunc nicht als Standartwert angeben ist. Wenn man dies nicht möchte sollte man deswegen an dieser Stelle aufpassen und
explizit ios_base::app oder ios_base::in angeben.
Wird zum Öffnen von Dateien verwendet:
|
C/C++ Quellcode
|
1
2
3
|
fstream.open (const char * Dateiname [, openmode Mode = ios_base::in | ios_base::out]);
ifstream.open(const char * Dateiname [, openmode Mode = ios_base::in]);
ofstream.open(const char * Dateiname [, openmode Mode = ios_base::out]);
|
Die Parameter sind dieselben wie beim Konstruktor, siehe weitere Infos
Konstruktor.
Wird zum Schließen einer geöffneten Datei verwendet:
|
C/C++ Quellcode
|
1
2
3
|
fstream.close ();
ifstream.close();
ofstream.close();
|
Sollte
immer dann Aufgerufen werden wenn keine Schreib- und/oder Leseoperationen mehr vorliegen.
Wird zur Statusüberprüfung verwendet, genauer ob eine Datei geöffnet werden konnte oder nicht:
|
C/C++ Quellcode
|
1
2
3
|
fstream.is_open ();
ifstream.is_open();
ofstream.is_open();
|
Die Funktion gibt
true zurück wenn eine Datei offen ist und
false wenn keine Datei offen ist bzw. nicht geöffnet werden konnte.
Die Positionierungsfunktionen verwendeten einen dynamischen integralen Datentypen streampos, der Einfachheit halber kann hier [unsigned] int angenommen werden.
Werden verwendet um die aktuelle
Schreibposition (tellput) bzw. die aktuelle
Leseposition (tellget) zu
ermitteln:
|
C/C++ Quellcode
|
1
2
3
4
5
|
fstream.tellg();
fstream.tellp();
ifstream.tellg();
ofstream.tellp();
|
Rückgabe ist die aktuelle Position vom Typ streampos.
Werden verwendet um die aktuelle
Schreibposition (seekput) bzw. die aktuelle
Leseposition (seekget) zu
verschieben bzw. zu
positionieren:
|
C/C++ Quellcode
|
1
2
3
4
|
ifstream.seekg(streampos Position)
ifstream.seekg(streampos Offset, seekdir Start);
ofstream.seekp(streampos Position)
ofstream.seekp(streampos Offset, seekdir Start);
|
Diese Funktionen sind auch beim
fstream-Objekt verfügbar.
Parameter Position: Gibt die absolute Position, vom Anfang der Datei an.
Parameter Offset: Relative Positionsverschiebung, von Start abhängig. (Negative Werte erlaubt)
Parameter Start: Gibt die Startposition an. Gültige Werte, können
nicht kombiniert werden:
- ios_base::beg (begin) Vom Anfang der Datei.
- ios_base::cur (current) Von der aktuellen Position, siehe tellp/tellg.
- ios_base::end (end) Vom Ende der Datei.
Wird verwendet um einzelne Bytes an die aktuelle Schreibposition zu schreiben, dabei wird, falls vorhanden, das Byte überschrieben und die
Schreibposition um eins verschoben:
|
C/C++ Quellcode
|
1
2
|
fstream.put (char Byte);
ofstream.put(char Byte);
|
Parameter Byte: Das zu schreibende Byte.
Wird verwendet um Datenblöcke an die aktuelle Schreibposition zu schreiben, dabei wird, falls vorhanden, Daten überschrieben und die
Schreibposition um die Datenblocklänge verschoben:
|
C/C++ Quellcode
|
1
2
|
fstream.write (const char *Daten, streamsize Laenge);
ofstream.write(const char *Daten, streamsize Laenge);
|
Parameter Daten: Ein Zeiger im Speicher auf die zu schreibenden Daten.
Parameter Länge: Die Länge der zu schreibenden Daten. Dynamischer Integraler Datentyp kann der Einfachheit halber als
int angesehen werden.
Der
Schreib-Operator << ist ein mächtiges Werkzeug um alle gängigen Typen in eine Datei zu schreiben sowie benutzerdefinierte Typen elegant zu schrieben, auf letzteres wird in den folgenden Artikel noch genauer eingegangen.
Die Daten werden an die aktuelle Schreibposition geschrieben, vorhandene Daten werden gegebenenfalls überschrieben und die
Schreibposition wird um die Datengröße automatisch verschoben.
|
C/C++ Quellcode
|
1
2
|
fstream << bool(true) << int(8) << float(3.14) << (void*)(0xFFFF) << char('Z') << "Hello World" << endl;
ofstream << bool(true) << int(8) << float(3.14) << (void*)(0xFFFF) << char('Z') << "Hello World" << endl;
|
Wie man sieht kommt der Schreiboperator dem der Konsole (
cout) nahe und man kann größtenteils auch die Befehle der Konsole verwenden.
Wird verwendet um ein Byte von der aktuellen Position einzulesen, dabei wird die
Leseposition um eins verschoben:
|
C/C++ Quellcode
|
1
2
|
fstream.get ();
ifstream.get();
|
Rückgabe ist eine
int-Repräsentation des eingelesenen Bytes.
Alternativer Funktionsaufruf:
|
C/C++ Quellcode
|
1
2
|
fstream.get (char &Byte);
ifstream.get(char &Byte);
|
Parameter Byte: Variable in die das eingelesene Byte gespeichert werden soll.
Wird größtenteils bei Textdateien verwendet um eine Zeile, beginnend von der aktuelle Leseposition, einzulesen, dabei wird die
Leseposition automatisch um die Anzahl der gelesenen Bytes verschoben:
|
C/C++ Quellcode
|
1
2
|
fstream.getline (char* Daten, streamsize Laenge [, char Trennzeichen]);
ifstream.getline(char* Daten, streamsize Laenge [, char Trennzeichen]);
|
Parameter Daten: Ein Zeiger im Speicher an dem die gelassenen Daten abgelegt werden sollen.
Parameter Länge: Größe des Speicherbereichs der Daten. Dynamischer Integraler Datentyp kann der Einfachheit halber als
int angesehen werden.
Parameter Trennzeichen: Optional, Zeichen bis zu welchem eingelesen werden soll. Falls nicht angegeben wird das betriebssystemabhängige Zeichen für Zeilenumbruch verwendet.
Wird verwendet um Datenblöcke von der aktuellen Leseposition zu lesen, dabei wird die
Leseposition um die Anzahl der gelesenen Bytes verschoben:
|
C/C++ Quellcode
|
1
2
|
fstream.read (char *Daten, streamsize Leange);
ifstream.read(char *Daten, streamsize Leange);
|
Parameter Daten: Ein Zeiger im Speicher an dem die gelesenen Daten abgelegt werden sollen, muss
größer gleich der Größe der zu lesenden Datengröße sein.
Parameter Länge: Die Länge der zu lesenden Daten. Dynamischer Integraler Datentyp kann der Einfachheit halber als
int angesehen werden.
Der
Lese-Operator >> ist ein mächtiges Werkzeug um alle gängigen Typen aus einer Datei zu lesen sowie benutzerdefinierte Typen elegant zu lesen, auf letzteres wird in den folgenden Artikel noch genauer eingegangen.
Die Daten werden von der aktuellen Leseposition gelesen und die
Leseposition wird um die Datengröße automatisch verschoben.
|
C/C++ Quellcode
|
1
2
|
fstream >> Bool >> Int >> Char;
ifstream >> Bool >> Int >> Char;
|
Wie man sieht kommt der Leseoperator dem der Konsole (
cout) nahe und man kann diesen größtenteils genauso handhaben.
- Arbeiten mit Dateien Teil 2: Textdateien (demnächst)