Dateien

Unvergesslich

Ins eine Ohr rein, aus dem anderen raus. Wir hoffen natürlich nicht, dass dieser Satz deine bisherige Lektüre unserer Webseite beschreibt. Allerdings bringen diese Worte ziemlich genau auf den Punkt, wie dein Computer mit Informationen umgeht. Wenn du Daten anlegst, seien es Integer-, Float- oder Double-Werte, behält dein Computer sie nur solange im Gedächtnis, wie dein Programm läuft. Einmal beendet und schon ist jegliche Erinnerung an deine Werte verschwunden.

Nicht, dass es schlecht wäre. Dein Computer will effizient arbeiten und den wertvollen Speicher nicht mit allem zumüllen. Doch manchmal wäre es gar nicht so schlecht, bestimmte Werte dauerhaft speichern zu können. Eine Liste mit all deinen Schulnoten beispielsweise. Die Temperaturen des letzten Monats. Wenn du mit diesen Werten längerfristig arbeiten möchtest, brauchst du Wissen über Dateien.


Eine Datei kannst du dir in erster Linie wie einen char-Array vorstellen, in das du einerseits Informationen schreiben kannst, aus dem du auf der anderen Seite aber auch Informationen lesen kannst. Um mit Dateien in deinem Programm umgehen zu können, brauchst du zuerst jedoch einen kleinen Sekretär, der mit allen relevanten Funktionen und Methoden ausgestattet ist und dir dadurch den Gebrauch von Dateien erst ermöglicht. Dieser Sekretär ist die Klasse „fstream“, die in der Bibliothek <fstream> enthalten ist. Wie bei Strings musst du also vor dem Benutzen von Dateien eine Bibliothek öffnen:


    #include <fstream>

    fstream f;


In diesem Programmschnipsel legst du also ein Objekt des Typs fstream an, stellst also deinen Sekretär ein, der Dateizugriffe für dich realisiert. 


Die vier wichtigsten Operationen, die du für den Umgang mit Dateien benötigst, sind das Öffnen, Schreiben, Lesen und Schließen. Gehen wir diese Funktionen mal nacheinander durch.


Am Anfang eines Datei-Programms steht immer das Öffnen. Dieses realisierst du wie folgt:


    f.open(Pfadname, Modus);


Unter „Pfadname“ kannst du entweder den Namen der benötigten Datei („daten.txt“) oder seinen absoluten Dateipfad angeben („/users/db01/klaus/daten.txt“ oder „c:\\programs\\readme.txt“). Mit „Modus“ hingegen teilst du deinem Computer mit, was genau du mit der geöffneten Datei eigentlich anstellen willst. Da stehen dir viele Möglichkeiten offen: Du kannst schließlich eine Datei einfach nur zum Lösen öffnen oder sie aber zum Schreiben benötigen. Wie die genaue Syntax aussieht und welche weiteren Modi man hier unterscheidet, kannst du dieser Übersicht entnehmen:

Übrigens wäre es auch nicht verkehrt, mehrere Modi anzugeben. Dies geschieht wie folgt:


    fstream messdaten;

    messdaten.open(„daten.txt“, ios::in | ios::out);


Bevor du nun aber frohlockend ein Programm zum Öffnen von Dateien schreibst, musst du vorher noch eine Sache beachten: Was, wenn sich die gewünschte Datei gar nicht öffnen lässt? Immerhin kann es vorkommen, dass du gar keine Zugriffsrechte auf die Datei hast oder die Datei im schlimmsten Falle nicht mal existiert. Deswegen ist es ratsam, wenn nicht gar essentiell, den Fehlerfall immer abzuprüfen:


    if (f.fail()){

        cout << „Fehler beim Öffnen der Datei.“ << endl;

    }


Die Methode, die uns hier vor einem Crash des Programms rettet, ist fail(). 


Nun gehen wir aber mal davon aus, dass es beim Öffnen der Datei zu keinen Fehlern gekommen ist. Wir können uns also problemlos dem Schreiben, der Ausgabe von Informationen in Dateien widmen. Sprich, wir beantworten die Frage: Wie stopfen wir unsere Werte in eine Datei hinein? Dies geschieht auf ähnliche Weise wie bei der Ausgabe von Werten auf den Bildschirm:


    string vorname=“Gerald“;

    string nachname=“Ülmer-Neumann“

    fstream f;

    f.open(„daten.txt“, ios::out);

    f<<vorname << endl;

    f<<name<<endl;

Wie beim Befehl „cout“ kommt auch hier der Ausgabeoperator („<<“) zum Einsatz.


Haben wir unsere Datei einmal mit Daten gefüllt, wollen wir sie vielleicht irgendwann mal ja wieder lesen. Und auch beim Einlesen sind die Ähnlichkeiten zur Eingabe mit „cin“ unverkennbar:


    fstream f2;

    f.open(„daten.txt“, ios::in);

    f2>>vorname;


Die Ein- und Ausgabe wirst du häufig mit Schleifen verknüpfen. Bevor du das umsetzen kannst, brauchst du aber noch eine weitere pfiffige Methode: eof(). Die Bedeutung dieser Funktion wird dir vermutlich erst einleuchten, wenn wir verraten, wofür diese drei Buchstaben stehen: Nämlich für „End of file“. eof() prüft, ob das Dateiende während des Lesens oder Schreibens erreicht ist. Die Verwendung dieser Methode kann beispielsweise wie folgt aussehen:


    fstream f;

    int i=0;

    messwerte[1000];

    while(!f.eof() && i<1000){

        f >> messwerte[i];

        i++;

    }


Widmen wir uns nun der letzten wichtigen Funktion, dem Schließen von Dateien. Nachdem man nämlich seinen Spaß mit Dateien hatte, muss die Verbindung zwischen Datei und Programm irgendwie gekappt werden. Die dafür notwendige Syntax ist leicht und einprägsam:


    f.close();


Üblicherweise werden Dateien mit dem Ende der main-Funktion geschlossen. Soll eine Datei jedoch schon vorher geschlossen werden, empfiehlt sich die Verwendung der close-Methode. 


Wir behaupten: Beim Thema Dateien kommen Bücherliebhaber auf ihre Kosten. Denn der ganze Umgang mit Dateien ähnelt dem beim Lesen von Büchern: Man kann ein Buch aus dem Regal nehmen, es öffnen, sich Informationen herauspicken, sogar Notizen und Anmerkungen in ein Buch schreiben und zu guter Letzt wird man es wieder zurück ins Regal packen. Auf dieselbe Art und Weise funktionieren auch Dateien. Da sage doch mal einer, Informatiker mögen keine Bücher. 

Aufgaben: Dateien

A1: Quiz dich schlau!

Created with Sketch.

Nachfolgend sind einige Fragen aufgeführt, die du nach diesem Abschnitt beantworten können solltest:

(1) Wie erfolgt das Öffnen von Dateien? Welche Methode wird verwendet? Welche Parameter müssen beim Aufruf dieser
Methode angegeben sein?

(2) Woran erkennt man, ob das Öffnen einer Datei erfolgreich war und wie soll man in einem Programm auf den Fehlerfall reagieren?

(3) Warum sollte im Fehlerfall (der Methode open) das Programm nicht einfach fortgesetzt werden? Welche Folgefehler kannst du dir vorstellen?

(4) Kennst du die Befehle zum Schreiben von Zeichen und Zahlen in eine Datei und zum Auslesen aus einer Datei?

(5) Wie wird eine Datei geschlossen? Welche Methode wird dazu eingesetzt?

(6) Aus welchem Grund sollte man Dateien explizit schließen, auch wenn das bei Programmende automatisch erfolgt?

A2: Spieglein, nielgeipS

Created with Sketch.

Schaust du in den Spiegel, so eröffnet sich dir eine neue Welt: die Spiegelwelt. Hier laufen alle Dinge anders als normal ab: Da sind die Letzten die Ersten und die Ersten die Letzten. Um einen Eindruck hierfür zu bekommen, schreibe zunächst einen kleinen Text in eine Datei der Endung .txt, .dat, o. ä. (mit Editor etc.) und anschließend einen C++-Code, das diese Datei einliest und den Text in umgekehrter Reihenfolge wieder ausgibt. Das heißt, das letzte Symbol soll nun das erste sein.

Hinweis: Auf die Spiegelung einzelner Buchstaben muss verzichtet werden. Weiterhin soll die Groß- und Kleinschreibung des Ursprungstextes gleich bleiben.

A3: Kontaktformular

Created with Sketch.

Wenn du nach unten scrollst oder auf die Home-Seite gehst, wirst du ein Kontaktformular vorfinden (Nicht der Fall? Bitte kontaktiere uns!). Damit auch du selbst ein solches in C++ programmieren kannst, schreibe bitte ein solches Formular, bei dem folgendes eingelesen wird

Name:
Email-Adresse:
Geburtsdatum:
Betreff:
Nachrichtentext:


Alle eingelesenen Angaben sollen in eine Datei gespeichert werden. 

A4: Veranstaltungsverwaltung, cont. 

Created with Sketch.

Auf der vorherigen Seiten hast du ein C++-Programm für die Konzertverwaltung programmiert. Dieses Programm konnte bisher nur die gerade eingegebenen Daten anzeigen.
In diesem Kapitel hast du die Möglichkeiten zur Speicherung in Dateien und dem Lesen von Daten aus Dateien kennengelernt. Damit kann jetzt die Veranstaltungsverwaltung vervollständigt werden.

Schreibe bitte zwei weitere Funktionen, die erste soll alle Daten der Liste der Veranstaltungen in eine Datei speichern, die Signatur der Funktion ist:
void writeEventsInFile(string dateiname, ev list[], int evnumberElemente);
Die zweite Funktion soll die Daten aus dieser Datei einlesen und in dem internen Array speichern. Die Signatur dieser Funktion ist:
int readEventsFromFile(string dateiname, ev list[]);
Die Form, wie die Dateien in die Datei gespeichert werden sollen, kannst du selbst festlegen. 

Zu Beginn des Programms sollen alle Daten aus der Datei geladen werden, bei Beenden des Programms sollen alle Inhalte des Array in die Datei gesichert werden. Binde bitte die beiden neuen Funktionen dementsprechend in die Veranstaltungsverwaltung ein. 

Musterlösungen

L1: Quiz dich schlau! 

Created with Sketch.

(1) fstream f;
      f.open("daten.txt", ios::in)

Methode mit Parameter: open(Pfadname, Modus)

(2) Laufzeitfehlern führt zum Programmabsturz; daher sollte der Fehlerfall abgefangen werden durch:
- die Ausgabe einer Fehlermeldung und das Beenden des Programms
- das Einlesen eines anderen Dateinamens
- das Anlegen der Datei in einem anderen Verzeichnis.

(3) Wenn man eine Datei nicht öffnen kann, kann man später auch keine Daten in diese Datei speichern. Es kommt dadurch also zu weiteren Fehlern (Laufzeitfehlern) im Programm, die zum Programmabsturz führen können.

(4) Schreiben in Datei: Im Gegensatz zur Funktion Ausgabe auf den Bildschirm steht zu Beginn des Befehls das Dateiobjekt (f << "Dieser Text geht in die Datei";).
      Auslesen aus Datei: f >> text;

(5) Mit der Methode close() wird die entsprechende Datei geschlossen, also f.close(); .

(6) Wenn die Arbeit mit einer Datei beendet ist, sollte diese aber vorher explizit im Programm geschlossen werden. Ein Grund dafür ist, dass die Anzahl der File‐Pointer eines Programms begrenzt ist. Ein weiterer Grund ist, dass man erst nach dem Schließen von Dateien garantieren kann, dass alle Daten gespeichert wurden. Wurden Daten in eine Datei geschrieben und diese wurde bereits geschlossen, dann weiß man, dass auch im Falle eines späteren Fehlers im Programm die Daten in der Datei korrekt gespeichert sind. Wurde die Datei noch nicht geschlossen, so kann man das nicht garantieren.

L2: Spieglein, nielgeipS

Created with Sketch.

#include <iostream>
#include <fstream>
#include <string>
using namespace std;


int main(){
    string DateiName = "loremipsum.txt";
    fstream text;
    char buchstabe;
    string strtext, strhilf;

 text.open(DateiName, ios::in);
 if (!text) {
    cout << "Fehler beim Oeffnen der Datei";
    return(0);
 }

 while (!text.eof()){
//Speichere den Text in einen String
    text >> strhilf;
//Die Datei text gibt stets wortweise (bis Leerzeichen) aus
    strtext.append(strhilf);
    strtext.append(" ");
//Beachte: Am Ende wird ein Leerzeichen zu viel angehängt
}

text.close();

for (int i = strtext.size() - 2; i > -1; i--){
//-2, da siehe letzten Kommentar
    cout << strtext[i];
}

cin.clear();
//damit sich das Konsolenfenster nicht sofort wieder schließt
cin.get();
return(0);
}

L3: Kontaktformular 

Created with Sketch.

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main() {
    string DateiName = "kontakt.txt";
    fstream kontakt;
    string name, geburtsjahr, email, betreff, text;

    kontakt.open(DateiName, ios::out);
    if (!kontakt) {
         cout << "Fehler beim Oeffnen der Datei ";
         return 0;
     }
    cout << "Name: ";
    getline(cin, name);
    cout << "Email-Adresse: ";
    getline(cin, email);
    cout << "Geburtstdatum: ";
    getline(cin, geburtsjahr);
    cout << "Betreff: ";
    getline(cin, betreff);
    cout << "Nachrichtentext: ";
    getline(cin, text);

    kontakt << name << endl;
    kontakt << email << endl;
    kontakt << geburtsjahr << endl;
    kontakt << betreff << endl;
    kontakt << text << endl;
    kontakt.close();

    return(0);
}

L3: Veranstaltungsverwaltung, cont. 

Created with Sketch.

#include <iostream>
#include <string>
#include <iomanip>
#include <fstream>

using namespace std;

typedef struct {
    string artist;
    string concert;
    float preis;
    int year;
    int month;
    int day;
    string city;
} ev;

void printEvents(ev list[], int evnumberElemente) {
    int i;
    cout << endl << " Veranstaltungen " << endl << endl;
    for (i = 0; i < evnumberElemente; i++) {
        cout << setw(12) << list[i].artist;
        cout << setw(15) << list[i].concert;
        cout << setw(10) << list[i].city;
        cout << setw(3) << list[i].day;
        cout << setw(3) << list[i].month;
        cout << setw(5) << list[i].year;
        cout << setw(6) << "  Preis: " << list[i].preis << " Euro " << endl;
    }
    return;
}

int readEvents(ev list[200], int evnumberElemente) {
    char answer;
    do {
        cout << endl << "Bitte einen Kuenstlernamen eingeben: ";
        cin >> list[evnumberElemente].artist;

        cout << endl << " Bitte einen Konzertname eingeben: ";
        cin >> list[evnumberElemente].concert;
        cout << endl << " Bitte einen Ort eingeben: ";
        cin >> list[evnumberElemente].city;
        cout << endl << " Bitte ein Datum eingeben:  (day month year) ";
        cin >> list[evnumberElemente].day;
        cin >> list[evnumberElemente].month;
        cin >> list[evnumberElemente].year;
        cout << endl << " Bitte den Preis eingeben: ";
        cin >> list[evnumberElemente].preis;

        evnumberElemente++;
        cout << endl << endl << "\n\n Weiteren Datensatz eingeben? (j/n) ";
        fflush(stdin);
        cin >> answer;
    } while (answer != 'n');
    return(evnumberElemente);
}

void writeEventsInFile(string dateiname,
    ev list[], int evnumberElemente) {   
    fstream evFile;   
    int i;

    evFile.open(dateiname, ios::out);   
    if (evFile){   
        for (i = 0; i<evnumberElemente; i++) { 
            evFile << list[i].artist << " ";   
            evFile << list[i].concert << " ";
            evFile << list[i].city << " ";
            evFile << list[i].day << " " << list[i].month << " " << list[i].year << " ";
            evFile << list[i].preis << endl;
        }
    }
    evFile.close();
    return;
}

int readEventsFromFile(string dateiname, ev list[]) {     
    fstream evFile;         
    int i = 0;
    evFile.open(dateiname, ios::in);   
    if (!evFile) {
        return(0);
    }
   else{
       do {
           evFile >> list[i].artist;     
           evFile >> list[i].concert;
           evFile >> list[i].city;
           evFile >> list[i].day;
           evFile >> list[i].month;
           evFile >> list[i].year;
           evFile >> list[i].preis;
           i++;
       } while (!evFile.eof());   
       evFile.close();       
       return(i - 1);
    }
}

void printEventsForCity(ev list[], int evnumber, string city) {
   cout << endl << endl << "Veranstaltungen in " << city << ":" << endl;

    for (int i = 0; i<evnumber; i++) {
        // Vergleich mit Citysangabe beim Konzert

        if (city == list[i].city) {
            cout << setw(12) << list[i].artist;
            cout << setw(15) << list[i].concert;
            cout << setw(3) << list[i].day << setw(3) <<
            list[i].month << setw(5) << list[i].year << endl;
        }
    }
}

void printEventsForCityAndDate(ev list[], int evnumber, string city, int month, int year) {
    cout << endl << endl << "Veranstaltungen in " << city << ":" << endl;

    for (int i = 0; i<evnumber; i++) {
        // Vergleich mit Citysangabe beim Konzert

        if ((city == list[i].city) && (list[i].month == month) && (list[i].year== year)) {
            cout << setw(12) << list[i].artist;
            cout << setw(15) << list[i].concert;
            cout << setw(3) << list[i].day << setw(3) <<
            list[i].month << setw(5) << list[i].year << endl;
        }
    }
}

void printEventsForArtist(ev list[], int evnumber, string artist) {
    cout << endl << endl << "Veranstaltungen von " << artist << ":" << endl;
    for (int i = 0; i<evnumber; i++) {
        // Vergleich mit Künstlernamen beim Konzert
        if (list[i].artist.find(artist)!=-1) {
            cout << setw(10) << list[i].city;
            cout << setw(15) << list[i].concert;
            cout << setw(3) << list[i].day << setw(3) <<
            list[i].month << setw(5) << list[i].year << endl;
        }
    }
}

int countEventsForArtist(ev list[], int evnumber, string artist) {
    int counter = 0;
    for (int i = 0; i<evnumber; i++) {
        // Vergleich mit Künstlernamen beim Konzert
        if (list[i].artist.find(artist) !=-1) {
            counter++;
        }
    }
    return(counter);
}

int main() {
    string dateiname = "events.txt";
    ev list[200];
    int evnumber = 0;
    char antwcity;
    int auswahl;
    string artist, city;
    int month, year;

    evnumber = readEventsFromFile(dateiname, list);

    do {
        cout << endl << endl << " 1 - Ausgabe aller Veranstaltungen" << endl;
        cout << " 2 - Ausgabe aller Veranstaltungen eines Kuenstlers" << endl;
        cout << " 3 - Ausgabe aller Veranstaltungen in einer Stadt" << endl;
        cout << " 4 - Ausgabe aller Veranstaltungen in einer Stadt zu einem Termin" << endl;
        cout << " 5 - Eingabe einer weiteren Veranstaltung" << endl;
        cout << endl;
        cout << "     Ihre Auswahl (Ende mit 0)   ";
        cin >> auswahl;

        if (auswahl == 1) {
            printEvents(list, evnumber);
        }
        else if (auswahl == 2) {
            cout << "Gesuchten Kuenstler eingeben: ";
            cin >> artist;
            printEventsForArtist(list, evnumber, artist);
            cout << endl << "   Der Kuenstler hat " <<  countEventsForArtist(list, evnumber, artist) << " Veranstaltung(en) " << endl;
        }

        else if (auswahl == 3) {
            cout << "Gesuchten Ort eingeben: ";
            cin >> city;
            printEventsForCity(list, evnumber, city);
        }
        else if (auswahl == 4) {
            cout << "Gesuchten Ort eingeben: ";
            cin >> city;
            cout << "Gesuchten Monat eingeben: ";
            cin >> month;
            cout << "Gesuchtes Jahr eingeben: ";
            cin >> year;
            printEventsForCityAndDate(list, evnumber, city, month, year);
        }
       else if (auswahl == 5) {
           evnumber = readEvents(list, evnumber);
           printEvents(list, evnumber);
        }
    } while (auswahl != 0);

    printEvents(list, evnumber);

    cout << "Datenbestand wurde in die Datei gespeichert";
    writeEventsInFile(dateiname, list, evnumber);
    return(0);
}




Fragen, Anregungen?

Ist etwas unverständlich oder du hast Fragen zu einem Thema, Aufgabe oder einfach zur C++-Welt? 
Dann schreibe uns einfach eine Mail!