27
Ein- und Ausgabe in Dateien (File I/O) Informatik für Elektrotechnik und Informationstechnik Benedict Reuschling [email protected] Hochschule Darmstadt Fachbereich Informatik WS 2013/14 Zuletzt aktualisiert: 13.01.2014, 08:27 Uhr

Ein- und Ausgabe in Dateien (File I/O) - Informatik für ... · Ein- und Ausgabe in Dateien (File I/O) Dateien in C++ lesen und schreiben Motivation Wir werden uns in diesem Kapitel

  • Upload
    donhu

  • View
    215

  • Download
    0

Embed Size (px)

Citation preview

Ein- und Ausgabe in Dateien (File I/O)Informatik für Elektrotechnik und Informationstechnik

Benedict [email protected]

Hochschule DarmstadtFachbereich Informatik

WS 2013/14

Zuletzt aktualisiert:13.01.2014, 08:27 Uhr

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Überblick

1 Dateien in C++ lesen und schreibenDateien lesen und schreibenFlags zum Öffnen von DateienStatus des Dateistroms prüfenStreamposition bestimmen und verändern

2 Eingaben korrekt verarbeitenWie kann ich in C++ . . .

2 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

MotivationWir werden uns in diesem Kapitel mit dem Lesen und Schreiben vonDateien beschäftigen. Mit den uns bereits bekannten Operationen lassensich Dateien bereits für die Eingabe lesen und auch schreiben, indemwir auf Betriebssystemebene die Ausgabe mit > in eine Datei umleiten:meinprogramm > dateiname.txt. Durch diese Befehl wird eine Dateidateiname.txt angelegt bzw. überschrieben, wenn diese Datei bereitsexistiert. Die ganze Ausgabe von meinprogramm wird nicht auf demBildschirm ausgegeben, sondern in dateiname.txt geschrieben.Mit >> wird an eine bestehende Datei angehängt, ohne deren Inhalt zuüberschreiben. Durch Umleiten der Eingabe kann aus einer Datei dieEingaben für ein Programm gelesen werden: meinprogramm <eingaben.txtDiese Operationen sind jedoch ausserhalb des Einflusses unseresC++-Programms und wir möchten diese Funktionalität aus unseremProgramm heraus selbst steuern. Dazu bedienen wir uns denDatei-Strömen (engl. file streams) zum lesen und schreiben von Dateien.wir betrachten dabei nur Textdateien.

3 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Dateien lesen und schreiben

Überblick

1 Dateien in C++ lesen und schreibenDateien lesen und schreibenFlags zum Öffnen von DateienStatus des Dateistroms prüfenStreamposition bestimmen und verändern

2 Eingaben korrekt verarbeitenWie kann ich in C++ . . .

4 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Dateien lesen und schreiben

Dateien zum Lesen und Schreiben öffnen

Über die Headerdatei fstream wird die Funktionalität zum Lesen undSchreiben von Dateien zur Verfügung gestellt. Soll nur geschriebenwerden, dann kann auch die Headerdatei ofstream genutzt werden.Ebenso kann, wenn nur gelesen werden soll, als Header ifstream zumEinsatz kommen.Der übliche Ablauf zum Lesen oder Schreiben von Dateien ist folgender:

Objekt zum Lesen oder Schreiben im Programm anlegenObjekt mit dem Dateinamen zum öffnen verküpfenText über das Objekt schreiben oder lesenDatei schliessen

5 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Dateien lesen und schreiben

Einfaches Beispiel zum Schreiben einer Datei

1 #include <fstream >2 using namespace std;34 int main() {5 ofstream fileout;6 fileout.open("beispiel.txt");7 fileout << "Das steht jetzt in der Datei.";8 fileout.close();9 return 0;10 }

In Zeile 5 wird ein Objekt angelegt, dass ähnlich wie cout oder cin dieAusgabe in die Datei übernimmt. Der Name des Objekts kann beliebigvergeben werden, so lange dieser noch nicht verwendet wird. Die Dateibeispiel.txt wird in Zeile 6 geöffnet und im aktuellen Verzeichnisangelegt, falls diese noch nicht existiert. Nun da die Datei geöffnetwurde, kann in Zeile 7 etwas in die Datei geschrieben werden. ZumSchluss muss noch die Datei geschlossen werden, damit allegepufferten Schreibvorgänge abgeschlossen werden (Zeile 8).

6 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Dateien lesen und schreiben

Einfaches Beispiel zum Lesen einer DateiNun wollen wir den gerade geschriebenen Inhalt der Datei wiederauslesen und auf dem Bildschirm anzeigen.1 #include <iostream >2 #include <fstream >3 #include <string >4 using namespace std;56 int main() {7 string dateizeile;8 ifstream fin;9 fin.open("beispiel.txt");10 getline(fin ,dateizeile);11 fin.close();12 cout << "Zeile in der Datei: " << dateizeile << endl;13 return 0;14 }

Wir benötigen einen String dateizeile, in dem wir die gelesene Zeileaus der Datei aufbewahren können. Eine komplette Zeile lässt sichmittels der Funktion getline einlesen. Es wird das mit der Dateiverknüpfte Objekt und der String, in den diese Zeile gespeichert werden,übergeben (Zeile 10). Nach dem Schliessen der Datei wird der geleseneString in Zeile 12 ausgegeben.

7 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Dateien lesen und schreiben

Variablen als DateinamenOft will man dem Benutzer die Möglichkeit geben, den Dateinamenselbst einzugeben. Dabei müssen zwei Dinge beachtet werden.

Unter Windows müssen die Pfadangaben mit \ (Backslash) vondenen der Programmiersprache unterschieden werden. Ausdiesem Grund müssen alle Vorkommen von \ durch \\ in Stringsersetzt werden.string dateiname = "c:\\temp\\meinedatei.txt";

Bei der Verwendung von open() wird ein String im C-Stil erwartet.Aus diesem Grund kann keine Variable vom Typ string ohneweiteres dort eingesetzt werden. Jedoch lässt sich ein solcherString über die Funktion c_str() umwandeln. So kannbeispielsweise der Dateiname vom Benutzer eingelesen werden:1 string dateiname;2 ifstream eingabe;3 cout << "Bitte geben Sie den Namen der Eingabedatei an: ";4 cin >> dateiname;5 eingabe.open(dateiname.c_str()); // umwandeln in C-String

8 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Flags zum Öffnen von Dateien

Überblick

1 Dateien in C++ lesen und schreibenDateien lesen und schreibenFlags zum Öffnen von DateienStatus des Dateistroms prüfenStreamposition bestimmen und verändern

2 Eingaben korrekt verarbeitenWie kann ich in C++ . . .

9 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Flags zum Öffnen von Dateien

Modi zum Öffnen von DateienDer Funktion open lässt sich ein weiterer Parameter übergeben, der dieArt (Modus) beschreibt, wie die Datei zu öffnen ist. Dies bestimmtmassgeblich, was mit der Datei passieren wird, so lange diese geöffnetist. Die folgenden Möglichkeiten stehen zur Verfügung:

Modus Beschreibungios::in Für Leseoperationen öffnen.ios::out Für Ausgabeoperationen öffnen.ios::binary Datei im binären Modus öffnen.ios::ate Die Startposition an das Ende (at end) der Datei setzen.

Falls dieser Modus nicht gesetzt ist, wird am Anfangder Datei begonnen.

ios::app AlleAusgabeoperationenwerdenamDateiendedurch-geführt, der neue Inhaltwird angehängt (engl.append).Funktioniert nur bei reinen Ausgabeoperationen.

ios::trunc Falls eine bereits existierende Datei bei Ausgabe-operationen geöffnet wird, wird der vorherige Inhaltgelöscht (engl. truncate = abschneiden, kürzen) unddurch den neuen Inhalt ersetzt.

10 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Flags zum Öffnen von Dateien

Standardflags verknüpfenDie folgende Tabelle zeigt die Standardeinstellungen für dieverschiedenen Dateiobjekte für den Fall, dass keine Flags bei open()angegeben werden:

Header Standardmodusofstream ios::outifstream ios::infstream ios::in | ios::out

Der letzte Eintrag in der Tabelle zeigt, dass sich die Flags über denbitweisen ODER-Operator (|) auch miteinander kombinieren lassen. Sowird beispielsweise über die folgende Anweisung

ofstream ausgabe("beispiel.txt", ios::out | ios::app);

die Datei beispiel.txt zum Schreiben geöffnet, der Text an denbestehenden Inhalt angehängt und das alles in einer einzigenAnweisung. Auf diese Weise lassen sich beispielsweise Logdateienanlegen, welche die Ausgaben des Programms über mehrere Aufrufehinweg protokollieren.

11 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Status des Dateistroms prüfen

Überblick

1 Dateien in C++ lesen und schreibenDateien lesen und schreibenFlags zum Öffnen von DateienStatus des Dateistroms prüfenStreamposition bestimmen und verändern

2 Eingaben korrekt verarbeitenWie kann ich in C++ . . .

12 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Status des Dateistroms prüfen

MotivationBeim Arbeiten mit Dateien kann eine ganze Menge schief gehen und wirals Programmierer sollten unsere Programme möglichst robust gegensolche Fehler machen. Nur so können wir uns darauf verlassen, dierichtigen Informationen aus den Dateien zu bekommen bzw. in dieseabzulegen. Die folgende Liste zeigt typische Probleme, auf die man inseinem Programm achten sollte:

Datei kann nicht geöffnet werden• ungültiger/nicht vorhandener Dateiname oder Pfad zur Datei• Keine Berechtigung zum Lesen/Schreiben der Datei• Schreiben unmöglich durch volles oder defektesDateisystem/Speichermedium

Der angegebene Modus wird vom Betriebssystem nicht akzeptiertNeue Daten können nicht in die Datei geschrieben werdenGelesene Datei ist im falschen Format (z.B. Buchstaben statt int)

Für diese Fälle stehen uns eine Reihe von Möglichkeiten zur Verfügung,den Status des Dateistroms zu überprüfen und ggfs. darauf zu reagieren.

13 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Status des Dateistroms prüfen

Prüfen, ob Datei geöffnet werden konnteDie Funktion is_open() gibt true zurück, wenn die Datei mit demStream-Objekt erfolgreich verknüpft werden konnte (egal ob gelesenoder geschrieben wird). Andernfalls wird false zurückgegeben. Dieslässt sich im Programm nutzen, um die Verarbeitung sofort zu beenden,sollte die Datei nicht geöffnet werden können.1 #include <fstream >2 #include <iostream >3 using namespace std;45 int main() {6 ofstream fout("beispiel.txt"); // Datei zum schreiben oeffnen7 if (fout.is_open ()) { // hat es geklappt?8 fout << "Das steht jetzt in der Datei.";9 fout.close(); // Datei schliessen10 } else cerr << "Konnte Datei nicht oeffnen!"; // Fehlerfall11 return 0;12 }

In der 7. Zeile wird geprüft, ob die Datei korrekt geöffnet werden konnte.In diesem Fall wird etwas in die Datei geschrieben und diese danachwieder geschlossen. Im Fehlerfall wird in Zeile 10 eine Fehlermeldungauf die Standardfehlerausgabe (cerr) des Betriebssystems übergeben.

14 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Status des Dateistroms prüfen

Zustand des Dateistroms abfragenDer Dateistrom kann während des Lese- oder Schreibvorgangsunerwarteten Änderungen unterworfen sein. Aus diesem Grund solltedessen Status ständig geprüft werden, um keine ungültigen Daten zuerzeugen. Die folgenden Funktionen stehen zur Prüfung desStreamzustandes zur Verfügung:

bad(): gibt true zurück, falls eine Lese- oder Schreibaktionfehlschlägt. Dies kann auftreten, wenn eine Dateigeschrieben wird, die nicht zum Schreiben geöffnetwurde oder das Dateisystem keinen freien Speicherplatzaufweist.

fail(): ist true, wenn die Fälle von bad() eintreten. Zusätzlichwird auf Formatierungsfehler geprüft, wenn z.B.Buchstaben statt der erwarteten Integer-Zahlen gelesenwerden.

eof(): true, für den Fall, das beim Lesen das Dateiende (engl.end of file) erreicht wurde.

good(): gibt false für alle Fällen zurück, in denen die obigenFunktionen true liefern.

15 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Status des Dateistroms prüfen

Beispiel zur Verwendung der Zustandsabfragen1 #include <iostream >2 #include <fstream >3 #include <cstdlib > // wegen exit()4 #include <string >5 using namespace std;67 int main() {8 string zeile;9 int zaehler = 0;10 ifstream fin("beispiel.txt");11 if (!fin.good()) {12 cerr << "Konnte Datei nicht oeffnen!";13 exit(-1);14 }15 while(getline(fin ,zeile))16 cout << ++ zaehler << ". Zeile: " << zeile << endl;17 fin.close();18 return 0;19 }

In Zeile 11 wird der Zustand des Streams geprüft und im Falle eineserfolglosen Öffnens eine Fehlermeldung ausgegeben und danach dasProgramm beendet. Ebenso wäre eine verkürzte Form if (!fin)möglich, um den Fehler abzufangen.

16 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Streamposition bestimmen und verändern

Überblick

1 Dateien in C++ lesen und schreibenDateien lesen und schreibenFlags zum Öffnen von DateienStatus des Dateistroms prüfenStreamposition bestimmen und verändern

2 Eingaben korrekt verarbeitenWie kann ich in C++ . . .

17 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Streamposition bestimmen und verändern

Streamposition

Sowohl ifstream als auch ofstream besitzen spezielle Zeiger innerhalbdes Datenstroms. Bei ifstream ist dies das Element, das bei dernächsten Eingabeoperation gelesen wird, weshalb es auch der getPointer genannt wird. Umgekehrt wird bei ofstream ein Zeiger auf diePositition, an der die nächste Schreiboperation durchgeführt wird,genutzt. Dieser wird als put-Pointer bezeichnet. Infstream sind beideverfügbar. Diese beiden Zeiger lassen sich über die folgendenFunktionen verändern:tellg() und tellp(): Geben jeweils die Position im Lese- (tellg())

und Schreibstrom (tellp()) als Integer zurück undbenötigen keine Argumente.

seekg() und seekp(): Ändert die Position der get- und put-Ströme.

18 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Streamposition bestimmen und verändern

Streamposition verändernVarianten von seekg() und seekp() sind seekg(position) undseekp(position). Mit position wird der Zeiger im Strom auf dieseabsolute Position innerhalb der Datei gesetzt, beginnend vomDateianfang. Der Typ dieses Parameters ist der gleiche, wie er vontellg() und tellp() zurückgegeben wird: ein Integer.Weiterhin gibt es noch seekg(offset, direction) und seekp(offset,direction). Damit lässt sich die Position innerhalb des Streamsebenfalls verschieben und zwar beginnend von offset, der auch einenIntegerwert darstellt. Dies kann in die Richtung geschehen, die vondirection angegeben wird. Hier sind folgende Werte möglich:

direction Beschreibungios::beg Offset, der vom Beginn des Datenstroms an

gezählt wird.ios::cur Offset, der von der aktuellen Position des

Streampointers an gezählt wird.ios::end Offset, der vom Ende des Datenstroms an

gezählt wird.

19 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Streamposition bestimmen und verändern

Beispiel zu StreampositionenDieses Programm (mytellg) ermittelt die Dateigrösse, die bei nach derErstellung durch foutmit der Zeichenkette Test als Inhalt entsteht.1 #include <iostream >2 #include <fstream >3 using namespace std;45 int main () {6 long start ,ende;7 ofstream fout("beispiel.txt");8 fout << "Test";9 fout.close();1011 ifstream fin("beispiel.txt");12 start = fin.tellg(); // hole die Position vom Dateianfang13 fin.seekg (0, ios::end); // ans Dateiende springen14 ende = fin.tellg(); // hole die Position vom Dateiende15 fin.close();16 cout << "Dateigroesse: " << (ende -start) << " Bytes.\n";17 return 0;18 }

$ ./mytellgDateigroesse: 4 Bytes$ ls -lh beispiel.txt-rw-r--r-- 1 bcr staff 4B Dec 1 22:43 beispiel.txt 20 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Streamposition bestimmen und verändern

Lesen und Schreiben in der gleichen Datei1 #include <fstream >2 #include <iostream >3 #include <string >4 using namespace std;56 int main() {7 int i = 0;8 fstream FIO("FIO.txt", ios::out | ios::trunc);9 FIO.close(); // nun existiert FIO.txt als leere Datei10 FIO.open("FIO.txt", ios::in | ios::out);11 for ( ; i< 10; i++) // Zahlen in Datei schreiben12 FIO << i << ' ';13 FIO << endl;14 FIO.seekg (0); // zurueck zum Dateianfang15 while (!FIO >> i) // einlesen16 if (!FIO.eof()) cout << i << ' '; // Kontrollausgabe17 cout << endl;18 FIO.clear(); // EOF -Status loeschen19 FIO.seekp (5); // Position 5 suchen20 FIO << " Hallo "; // ueberschreiben21 FIO.seekg (0); // zurueck zum Dateianfang22 string puffer;23 getline(FIO ,puffer); // Zeile einlesen24 cout << puffer << endl; // und ausgeben25 FIO.close();26 return 0;27 } 21 / 27

Ein- und Ausgabe in Dateien (File I/O)Dateien in C++ lesen und schreiben

Streamposition bestimmen und verändern

Korrektes Einlesen bis zum DateiendeFalsch1 int i = 0;2 while (!cin.eof()) {3 cin >> x;4 i++;5 // ...6 }

Der EOF-Zustand wird im obigen Beispiel nicht in jedem Fall gesetzt,sondern evtl. erst nachdem ein Lesevorgang nach dem Dateiendeversucht wurde. Beispielsweise kann man beim Einlesen von Tastaturauch nicht erwarten, dass C++ in der Lage ist, vorherzusehen, ob nochZeichen kommen werden. Besser ist die folgende Schreibweise:

Richtig1 int i = 0;2 while (cin >> x) {3 i++;4 // ...5 }

22 / 27

Ein- und Ausgabe in Dateien (File I/O)Eingaben korrekt verarbeiten

Überblick

1 Dateien in C++ lesen und schreibenDateien lesen und schreibenFlags zum Öffnen von DateienStatus des Dateistroms prüfenStreamposition bestimmen und verändern

2 Eingaben korrekt verarbeitenWie kann ich in C++ . . .

23 / 27

Ein- und Ausgabe in Dateien (File I/O)Eingaben korrekt verarbeiten

Eingaben korrekt verarbeitenBeim Einlesen von Tastatur kann es vorkommen, dass der Benutzer einfalsches Zeichen (Buchstabe statt Zahl) eingibt. Hat man diese Eingabein eine Schleife gebaut, um gewisse Werte abzufangen, führt dieseEingabe zu einer Endlosschleife, wie im folgenden Fall:1 #include <iostream >2 using namespace std;3 int main() {4 int i = 0;5 cout << "Zahlen eingeben (Trenner: Space), Abbruch mit -1:";6 while (i != -1) {7 cin >> i;8 cout << "Sie haben " << i << " eingegeben .\n";9 }10 return 0;11 }

Zahlen eingeben (Trenner: Space), Abbruch mit -1: 2 3 aZahlen eingeben (Trenner: Space), Abbruch mit -1: Sie haben 0 eingegeben.Zahlen eingeben (Trenner: Space), Abbruch mit -1: Sie haben 0 eingegeben....

24 / 27

Ein- und Ausgabe in Dateien (File I/O)Eingaben korrekt verarbeiten

Die Lösung des ProblemsEine Möglichkeit, diesen Fehler zu umgehen, ist, die cin-Anweisung indie Abbruchbedingung der while-Schleife zu verschieben.1 #include <iostream >2 using namespace std;3 int main() {4 int i = 0;5 cout << "Zahlen eingeben (Trenner: Space), Abbruch mit -1:";6 while (cin >> i) {7 if (i == -1) break;8 cout << "Sie haben " << i << " eingegeben .\n";9 }10 return 0;11 }

Zahlen eingeben (Trenner: Space), Abbruch mit -1: 2 3 aSie haben 2 eingegeben.Sie haben 3 eingegeben.

In diesem Fall wird die while-Schleife abbrechen, wenn ein falschesZeichen oder -1 eingegeben wurde. Eine noch kürzere Schreibweiseergibt sich durch folgende Bedingung der while-Schleife:

while ((cin >> i) && (i != -1))25 / 27

Ein- und Ausgabe in Dateien (File I/O)Eingaben korrekt verarbeiten

Wie kann ich in C++ . . .

Überblick

1 Dateien in C++ lesen und schreibenDateien lesen und schreibenFlags zum Öffnen von DateienStatus des Dateistroms prüfenStreamposition bestimmen und verändern

2 Eingaben korrekt verarbeitenWie kann ich in C++ . . .

26 / 27

Ein- und Ausgabe in Dateien (File I/O)Eingaben korrekt verarbeiten

Wie kann ich in C++ . . .

Wie kann ich in C++ . . .Frage:

Wie kann ich prüfen, ob eine bestimmte Taste vor dem ENTER beider Eingabe gedrückt wurde?Wie kann ich erreichen, dass die eingegebenen Zeichen nicht aufdem Bildschirm ausgegeben werden (für Passworteingaben)?Wie kann ich den Cursor auf dem Bildschirm platzieren?Wie kann ich den Bildschirminhalt löschen?Wie kann ich die Farben auf dem Bildschirm ändern?

Antwort:Dies ist keine Standard-Funktion von C++. C++ erwartet weder eineangeschlossene Tastatur noch einen Bildschirm (denken Sie anembedded-Geräte, die das auch nicht benötigen). JederC++-Compilerhersteller implementiert diese Funktionen für dieverschiedenen unterstützten Betriebssysteme anders. Lesen Sie diedazugehörige Dokumentation!

27 / 27