Ein- und Ausgabe

Als Einstieg in die IOStreams mal ein Überblick, was man damit so machen kann. Wir erstellen Histogramme von Textdateien.

Video

Quellcode

#include <fstream>
#include <iostream>
#include <map>
#include <locale>
#include <iomanip>

using namespace std;

int main(int argc, char *argv[])
{
    if (argc != 2) {
        cerr << "Bitte Dateiname angeben!\n";
        return 1;
    }

    ifstream i(argv[1], ios_base::in);
    if (!i) {
        cerr << "Datei " << argv[1] << " konnte nicht geöffnet werden.\n";
        return 2;
    }
 
    auto &ctf = use_facet<ctype<char>>(i.getloc());
    map<char, size_t> counters;
    size_t total_alpha = 0;
    char c;
    while (i.get(c)) {
        if (ctf.is(ctype_base::alpha, c)) {
            counters[ctf.tolower(c)]++;
            ++total_alpha;
        }
    }
    
    cout.precision(2);
    cout.setf(ios_base::fixed);
    for (auto item : counters) {
        cout << item.first << ": " << 
                setw(5) << static_cast<float>(item.second)*100/total_alpha << " %\n";
    }
    return 0;
}

Erkärung

Ziel des Programms ist die Ermittlung der relativen Häufigkeiten verschiedener Buchstaben in einer Textdatei. Dazu wird die Datei zuerst als std::ifstream-Objekt geöffnet (Zeile 16). Dieses Objekt kann man dann als bool interpretieren, indem man es einfach als Bedingung für eine if-Anweisung verwendet. Wird das Objekt als true interpretiert, so ist das Objekt gültig, liefert diese Umwandlung false, so konnte bspw. die Datei nicht geöffnet werden.

Um zu ermitteln, ob ein eingelesenes Zeichen überhaupt ein Buchstabe ist, holen wir uns eine std::ctype-Facette und nutzen dann immer wieder die Funktion std::ctype::is() mit der Maske ctype_base::alpha. Auf die Art liefert uns is() true, wenn das übergebene Zeichen ein Buchstabe ist.

Die while-Schleife von Zeile 26 bis 31 liest die Datei Zeichen für Zeichen ein. Das eigentliche Einlesen geschieht in der Bedingung der Schleife mit der Methode std::ifstream::get(). Diese Methode existiert in verschiedenen Varianten. Die hier verwendete speichert das gelesene Zeichen in der übergebenen Variable und liefert das ifstream-Objekt selbst zurück. Dieses zurückgelieferte Objekt wird dann wieder als bool interpretiert und liefert uns so die Schleifenabbruchbedingung, wenn keine weiteren Zeichen mehr lesbar sind.

Die relativen Häufigkeiten ermittelt das Programm, indem es für jedes Zeichen getrennt in einer std::map die absolute Häufigkeit zählt und am Ende durch die Gesamthäufigkeit teilt. Dazu wandeln wir in Zeile 37 den Wert erst in einen float um, um auch Nachkommastellen zu bekommen.

Die Ausgabe soll schön rechtsbündig formatiert sein. Zu diesem Zweck müssen wir einige Details des Ausgabestroms std::cout konfigurieren. Wir wollen 2 Nachkommastellen ausgeben, also setzen wir mittels std::ostream::precision() in Zeile 33 die passende Anzahl. Um eine automatische Umwandlung der Zahl in die wissenschaftliche Darstellung zu verhindern, setzen wir mittels setf() noch das ios_base::fixed-Flag, was den Stream veranlasst, die Zahl komplett auszugeben. Zuguterletzt müssen wir noch die Rechtsausrichtung der Prozentzahlen sicherstellen. Da wir wissen, dass die längste Zahl 5 Stellen hat (2 Vorkommastellen, zwei Nachkommastellen und das Komma selbst), setzen wir mittels des Manipulators std::setw() die Feldbreite für das nächste auszugebende Objekt auf 5 und geben direkt im Anschluss die Prozentzahl aus. Standardmäßig füllt std::ostream links mit Leerzeichen bis zur Feldbreite auf, letztlich wird also die Ausgabe dadurch rechtsbündig.