Projekt 2: "Animierte ASCII-Kunst"

Website: Lerne ELEKTROTECHNIK und PROGRAMMIEREN
Kurs: Kurs zum Buch "Lerne Programmieren mit Projekten: C++"
Buch: Projekt 2: "Animierte ASCII-Kunst"
Gedruckt von: Gast
Datum: Saturday, 27. July 2024, 08:41

Beschreibung



1. Worum geht es?

In diesem Abschnitt wirst du dein zweites Programm entwickeln, in dem unter anderem die Klasse vector, das Einlesen von Dateien mit fstream und die for-Schleife eine entscheidende Rolle spielen werden. 

Ziel des Programms wird es sein, die Kommandozeile zum Abspielen von ASCII-Kurzfilmen zu nutzen. Die einzelnen Bilder bestehen dabei aus Textzeichen, die mit etwas Fantasie z.B. zwei sich küssende Katzen ergeben.


2. Datei zum Lesen öffnen

Worum geht es? 

In diesem Teil des Programms werden die Filmdaten von der Festplatte eingelesen und im Speicher deines Computers mit Hilfe geeigneter Datenstrukturen abgelegt. Im ersten Schritt wird versucht, die vom Nutzer angegebene Datei mit den Filmdaten zu öffnen. Sollte dies nicht funktionieren, dann beendet sich das Programm mit einer Fehlermeldung.

Was kannst du danach?

  • Datei zum Lesen öffnen
  • Fehler beim Öffnen erkennen

Der zu öffnende Dateiname kann hier eingegeben werden:
#include <string>
#include <iostream>
#include <fstream> // File-Stream

using namespace std;

int main()
{
    // Dateiname einlesen
    string filename;
    cout << "Bitte Filmdatei angeben : ";
    cin >> filename;
    cout << filename << endl;

    // Datei zum Lesen öffnen
    ifstream movie_data(filename);
    if (!movie_data.is_open())
    {
        cout << "Fehler beim Öffnen!" << endl;
        exit(1); // Programm mit Fehlercode beenden
    } else
    {
        cout << "Datei erfolgreich geöffnet!" << endl;
    }
    
    return 0; 
}

Die Datei mit dem Namen "datei1.txt" ist auf dem Server vorhanden. Verändere den Dateinamen im Eingabefeld und achte auf die Ausgabe des Programms.

3. Filmdaten zeilenweise auslesen

Worum geht es? 

Nachdem die Filmdatei im letzten Teil zum Lesen geöffnet wurde, wird sie in diesem Teil zeilenweise ausgelesen und der Inhalt in einer passenden Datenstruktur abgelegt. 

Nach Abschluss des Einlesevorgangs wird der Filmtitel sowie die Anzahl der Bilder ausgegeben.

Was kannst du danach?

  • Textdatei zeilenweise auslesen
  • Daten in (verschachtelten) Vektoren speichern
  • Teile eines Strings extrahieren


 Der zu öffnende Dateiname kann hier eingegeben werden:

Hinweis: Im Beispiel wurden nur die ersten 5 Bilder verwendet.
Die komplette Datei kann hier heruntergeladen werden.

#include <string>
#include <iostream>
#include <fstream>
#include <vector>

using namespace std;

int main()
{
    // Dateiname einlesen
    string filename;
    cout << "Bitte Filmdatei angeben : ";
    cin >> filename;
    cout << filename << endl;

    // Datei zum Lesen öffnen
    ifstream movie_data(filename);
    if (!movie_data.is_open())
    {
        cout << "Fehler beim Öffnen!" << endl;
        exit(1); // Programm mit Fehlercode beenden
    } else
    {
        cout << "Datei erfolgreich geöffnet!" << endl;
    }
    
    // Datenstrukturen für Filmdaten
    string title;                  // Titel des Films
    string line;                   // eine Zeile
    vector<string> image;          // ein Bild
    vector<vector<string>> images; // eine Bilderserie

    // Filmdaten zeilenweise auslesen
    while (getline(movie_data, line)) // zeilenweise auslesen
    {
        // Titel extrahieren
        if (line[0] == '@') // Markierung für Titel
        {
            title = line.substr(1, line.size() - 1);
        }

        // Aktuelle Zeile in Bild speichern
        if (line[0] == '"') // Markierung für Bilder
        {
            image.push_back(line);
        }

        // Fertiges Bild in Serie speichern
        if (line[0] == ' ' && image.size() > 0)
        {
            images.push_back(image); // Bild in Liste kopieren
            image.clear();           // Bild wieder löschen
        }
    }
    movie_data.close();
    cout << "Filmtitel = " << title << endl;
    cout << "Anzahl Bilder = " << images.size() << endl;
    
    return 0; 
}

4. Filmstatistiken berechnen

Worum geht es? 

Nachdem in den letzten Teilen die Filmdatei zum Lesen und deren Inhalt in einer passende Datenstruktur im Speicher abgelegt wurde, wird nun die Anzahl der Zeilen pro Bild sowie die Gesamtzahl der gespeicherten Zeichen bestimmt. Erstere ist Voraussetzung für die Höhenanpassung des Terminal-Fensters im nächsten Schritt. 

Was kannst du danach?

  • Eine for-Schleife benutzen
  • Auf die Elemente eines verschachtelten Vektors zugreifen

 Der zu öffnende Dateiname kann hier eingegeben werden:

Hinweis: Im Beispiel wurden nur die ersten 5 Bilder verwendet.
Die komplette Datei kann hier heruntergeladen werden.

#include <string>
#include <iostream>
#include <fstream>
#include <vector>

using namespace std;

int main()
{
    // Dateiname einlesen
    string filename;
    cout << "Bitte Filmdatei angeben : ";
    cin >> filename;
    cout << filename << endl;

    // Datei zum Lesen öffnen
    ifstream movie_data(filename);
    if (!movie_data.is_open())
    {
        cout << "Fehler beim Öffnen!" << endl;
        exit(1); // Programm mit Fehlercode beenden
    } else
    {
        cout << "Datei erfolgreich geöffnet!" << endl;
    }
    
    // Datenstrukturen für Filmdaten
    string title;                  // Titel des Films
    string line;                   // eine Zeile
    vector<string> image;          // ein Bild
    vector<vector<string>> images; // eine Bilderserie

    // Filmdaten zeilenweise auslesen
    while (getline(movie_data, line)) // zeilenweise auslesen
    {
        // Titel extrahieren
        if (line[0] == '@') // Markierung für Titel
        {
            title = line.substr(1, line.size() - 1);
        }

        // Aktuelle Zeile in Bild speichern
        if (line[0] == '"') // Markierung für Bilder
        {
            image.push_back(line);
        }

        // Fertiges Bild in Serie speichern
        if (line[0] == ' ' && image.size() > 0)
        {
            images.push_back(image); // Bild in Liste kopieren
            image.clear();           // Bild wieder löschen
        }
    }
    movie_data.close();
    cout << "Filmtitel = " << title << endl;
    cout << "Anzahl Bilder = " << images.size() << endl;
    
    // Filmstatistiken berechnen
    int symbols{0}, lines{0};
    for (int i = 0; i < images.size(); i++)
    {
        // Anzahl Zeilen
        if (images[i].size() > lines)
        {
            lines = images[i].size(); // Maximum speichern
        }

        // Anzahl Zeichen
        for (int j = 0; j < images[i].size(); j++)
        {
            symbols += images[i][j].size();
        }
    }
    cout << "Anzahl Zeilen / Bild = " << lines << endl;
    cout << "Anzahl Zeichen = " << symbols << endl;
    
    return 0; 
}

Die Datei mit dem Namen "datei1.txt" ist auf dem Server vorhanden. Verändere den Dateinamen im Eingabefeld und achte auf die Ausgabe des Programms.

5. Film abspielen

Worum geht es? 

Im letzten Teil des Projekts werden wir die eingelesenen Bilder über die Kommandozeile abspielen. Damit das funktioniert, müssen zwei Bedingungen erfüllt sein:

  1. Der Zeitabstand zwischen den angezeigten Bildern muss sinnvoll gewählt werden, damit der Eindruck einer Animation entsteht.
  2. Die Höhe des Terminal-Fensters muss so angepasst werden, dass die Anzahl der sichtbaren Zeilen zum jeweiligen Film passt. 

Sobald die Höhe angepasst ist (nicht in der Browser-Version), werden in einer Schleife die einzelnen Bilder nacheinander im Terminal-Fenster abgespielt. Damit der Eindruck einer flüssigen Animation entsteht, muss allerdings für eine definierte Zeit gewartet werden, bevor das nächste Bild angezeigt wird. Nach Erreichen des letzten Bildes wird das Programm dann schließlich beendet. 

Da die Browser-Version einige Einschränkungen hat (kein Terminal-Fenster, keine Animation), solltest du den Code zusätzlich lokal auf deinem Rechner ausführen.

Was kannst du danach?

  • Mit einer Doppelschleife über einen 2D-Vektor laufen
  • Eine leere Eingabe (ENTER) von der Kommandozeile erkennen
  • Die Ausführung mit Hilfe der <thread>-Blibliothek pausieren

Der zu öffnende Dateiname kann hier eingegeben werden:

Hinweis: Im Beispiel wurden nur die ersten 5 Bilder verwendet.
Die komplette Datei kann hier heruntergeladen werden.

#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <thread>

using namespace std;

int main()
{
    // Dateiname einlesen
    string filename;
    cout << "Bitte Filmdatei angeben : ";
    cin >> filename;
    cout << filename << endl;

    // Datei zum Lesen öffnen
    ifstream movie_data(filename);
    if (!movie_data.is_open())
    {
        cout << "Fehler beim Öffnen!" << endl;
        exit(1); // Programm mit Fehlercode beenden
    } else
    {
        cout << "Datei erfolgreich geöffnet!" << endl;
    }
    
    // Datenstrukturen für Filmdaten
    string title;                  // Titel des Films
    string line;                   // eine Zeile
    vector<string> image;          // ein Bild
    vector<vector<string>> images; // eine Bilderserie

    // Filmdaten zeilenweise auslesen
    while (getline(movie_data, line)) // zeilenweise auslesen
    {
        // Titel extrahieren
        if (line[0] == '@') // Markierung für Titel
        {
            title = line.substr(1, line.size() - 1);
        }

        // Aktuelle Zeile in Bild speichern
        if (line[0] == '"') // Markierung für Bilder
        {
            image.push_back(line);
        }

        // Fertiges Bild in Serie speichern
        if (line[0] == ' ' && image.size() > 0)
        {
            images.push_back(image); // Bild in Liste kopieren
            image.clear();           // Bild wieder löschen
        }
    }
    movie_data.close();
    cout << "Filmtitel = " << title << endl;
    cout << "Anzahl Bilder = " << images.size() << endl;
    
    // Filmstatistiken berechnen
    int symbols{0}, lines{0};
    for (int i = 0; i < images.size(); i++)
    {
        // Anzahl Zeilen
        if (images[i].size() > lines)
        {
            lines = images[i].size(); // Maximum speichern
        }

        // Anzahl Zeichen
        for (int j = 0; j < images[i].size(); j++)
        {
            symbols += images[i][j].size();
        }
    }
    cout << "Anzahl Zeilen / Bild = " << lines << endl;
    cout << "Anzahl Zeichen = " << symbols << endl;
    
    // Bildhöhe anpassen (nur lokal sinnvoll)
    /*
    for (int i = 0; i < lines - 5; i++) // Statistik berücksichtigen
    {
        cout << endl;
    }
    cout << "Terminal bis Titel zusammenschieben, dann ENTERn";
    cin.ignore(); // evtl. vorhandene Daten in cin ignorieren
    cin.get();    // Auf Enter-Taste als Eingabe warten
    */
    
    // Film abspielen
    for (int i = 0; i < images.size(); i++)
    {
        // Aktuelles Bild ausgeben
        for (int j = 0; j < images[i].size(); j++)
        {
            cout << images[i][j] << endl;
        }

        // Ausführung für Animationsdauer anhalten
        this_thread::sleep_for(
            chrono::milliseconds(250)
            );
    }
    cout << "\nTHE END\n";
    
    return 0; 
}