Warm-ups (freiwillig)
Sie müssen nicht alle Warm-ups machen. Suchen Sie sich in der Tabelle das heraus, was Ihnen für eine Aufgabe fehlt:
| Übt | Hilft bei | |
|---|---|---|
| Warm-up 1 | In eine Datei schreiben | Lautstärke, Wiederherstellung |
| Warm-up 2 | Aus einer Datei lesen mit fread | Lautstärke, Wiederherstellung |
| Warm-up 3 | Eine Datei kopieren | Lautstärke, Wiederherstellung |
| Warm-up 4 | Eine Datei Zeichen für Zeichen verschlüsseln | Lautstärke |
Legen Sie für die Warm-ups eine eigene Datei an, zum Beispiel warmup_aufgaben_1.c für Übung 1, im selben Ordner wie Ihre Übungsaufgaben. Die meisten Warm-ups bauen aufeinander auf: Sie ergänzen die Datei Schritt für Schritt. Lösungen gibt es für die Warm-ups nicht. Unter „Wo zu finden?“ steht jeweils, wo das Thema in Vorlesung und Shorts vorkommt. Mehr dazu im FAQ zu den Warm-ups.
Warm-up 1: In eine Datei schreiben
Kommentieren Sie den bisherigen Code aus und kopieren Sie folgendes Code-Snippet:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
string hello = "Hello World!\n";
char buffer;
string file_name = "hello.txt";
// File-Pointer `hello_file` mit `fopen` mit Dateinamen `file_name` im Modus "w";
// EINFÜGEN (1)
// Wichtig: Immer prüfen, ob `fopen` erfolgreich einen entsprechenden File-Pointer erstellen konnte;
if (hello_file == NULL)
{
printf("ERROR accessing file: %s\n", file_name);
return 1;
}
// Inhalt von `hello` mit `fwrite` in `hello_file` schreiben;
// EINFÜGEN (3)
// Datei schließen mit `fclose`;
// EINFÜGEN (2)
return 0;
}EINFÜGEN (1): Erstellen Sie einen File-Pointer hello_file mit fopen im Modus "w" (Schreiben). In diesem Modus wird eine noch nicht existierende Datei automatisch erstellt. Eine bereits existierende Datei mit diesem Namen wird überschrieben. Den Dateinamen hello.txt können Sie über die bereits initialisierte Variable file_name übergeben.
EINFÜGEN (2): Am Ende des Programms muss eine geöffnete Datei immer mit fclose geschlossen werden.
EINFÜGEN (3): Nutzen Sie fwrite, um den in hello initialisierten String in die Datei zu schreiben. Lesen Sie ggf. noch einmal die erwarteten Argumente für fwrite im Handbuch nach. Die Anzahl der zu schreibenden Elemente der Größe char kennen wir in diesem Fall - diese erhalten wir mit strlen(hello). Erinnern Sie sich daran, dass ein String letztlich ein char-Pointer ist und daher bereits eine Adresse repräsentiert.
Nach dem Ausführen des Programms sollten Sie eine Datei hello.txt mit dem Inhalt Hello World! im Dateibrowser finden.
Wo zu finden?
Warm-up 2: Aus einer Datei lesen mit fread
Nachdem wir nun bereits eine Datei erstellt haben, können wir auch aus dieser Datei lesen.
Ändern Sie in Zeile 14 den Modus von fopen für den File-Pointer für hello_file, indem Sie statt "w" nun "r" (Lesen) übergeben, da wir nun mit dem Programm aus der Datei “hello.txt” lesen wollen. Kommentieren Sie den Aufruf von fwrite aus Warm-up 1 aus. Ansonsten können wir den bestehenden Code direkt weiterverwenden.
Wissen Sie noch, was fread zurückgibt? Ansonsten können Sie auch einmal einen Blick in den Abschnitt des Handbuchs werfen, der sich mit dem Rückgabewert von fread beschäftigt.
Was gibt
fread zurück?
Was gibt
fread zurück, wenn eine Datei vollständig eingelesen wurde?
Außerdem ist dort zu lesen, dass sich die mit fopen geöffnete Datei die Anzahl der erfolgreich gelesenen Bytes merkt. Bei weiteren Aufrufen der Funktion fread auf diese Datei werden daher jeweils die Bytes nach den bereits gelesenen Bytes eingelesen.
Was geschieht, wenn fread direkt in der Bedingung einer while-Schleife aufgerufen wird, z.B. so:
while (fread(&buffer, sizeof(char), 1, hello_file))
{
}Erklärung
while-Schleife lesen wir char für char, also jeweils ein Byte, aus der Datei hello_file aus. Der char wird jeweils in der Variable buffer zwischengespeichert. Daher hat in diesem Fall unsere Puffer-Variable auch die Größe von einem char, d.h. einem Byte. Wollten wir etwa den Umfang von 8 Bytes, d.h. 8 chars, auf einmal “puffern”, würden wir demnach etwa ein char-Array der Größe 8, also char buffer[8], benötigen. Natürlich müssten wir dann zusätzlich entweder die Größe (size) der gelesenen Daten oder die Anzahl der auf einmal zu lesenden Elemente (nmemb) entsprechend anpassen (siehe Handbuch). Sind alle Bytes gelesen, gibt fread schließlich 0 zurück, da 0 Elemente der Größe “1 Byte” gelesen wurden. Der zurückgegebene Wert 0 wird in der Bedingung als false interpretiert, weshalb die Schleife dann beendet wird.Geben Sie innerhalb der Schleife den Wert von buffer jeweils mit
printf("%c", buffer);aus. Es sollte also
Hello World!ausgegeben werden.
Wo zu finden?
Warm-up 3: Eine Datei kopieren
Anstatt die zwischengespeicherten Werte auf der Konsole auszugeben, können wir diese auch in eine neue Datei schreiben - also eine Kopie der gelesenen Datei erstellen.
Erstellen Sie einen zweiten File-Pointer hello_file_copy, der eine Datei mittels fopen im Modus w (Schreiben) erstellt. Übergeben Sie als Dateinamen "hello-copy.txt".
Vergessen Sie nicht zu prüfen, ob der return-Wert von fopen, also hello_file_copy, gleich NULL ist. Beenden Sie in diesem Fall das Programm. Fügen Sie am Ende zudem den Aufruf von fclose zum Schließen der Datei ein.
Schreiben Sie in der Schleife jeweils den Inhalt von buffer mit fwrite in die Datei hello_file_copy.
In der neu erstellten Datei “hello-copy.txt” sollte nach dem Ausführen nun auch Hello World! stehen. Ergänzen Sie den Inhalt von “hello.txt” und führen Sie das Programm erneut aus. Der zusätzliche Text sollte auch in “hello-copy.txt” zu sehen sein.
In welchem Fall ist also der Ansatz mit der while-Schleife vorteilhaft?
Wo zu finden?
Warm-up 4: Eine Datei Zeichen für Zeichen verschlüsseln
Wir lesen also letztlich Zeichen für Zeichen aus der Datei aus und schreiben wieder Zeichen für Zeichen in die neue Datei. Vielleicht hat es in Ihrem Kopf bereits zu rotieren begonnen: Ja, so etwas ähnliches hatten wir bereits in einer der Aufgaben! In Cäsar haben wir Zeichen für Zeichen über einen String iteriert und jeweils Zeichen für Zeichen auf der Konsole ausgegeben. Mit einem Unterschied: Zwischen diesen Schritten haben wir das Zeichen verändert.
Natürlich können wir auch unsere Puffer-Variable buffer verändern, bevor wir sie in die Datei schreiben.
Kopieren Sie sich die rotate() Funktion aus Cäsar in Ihre Datei und rufen Sie
buffer = rotate(buffer, 1);auf, bevor Sie den Wert von buffer in die Datei schreiben. Fügen Sie außerdem noch fehlende Header-Dateien und Konstanten zur Verwendung von rotate() ein.
Sie haben keine rotate-Funktion aus Cäsar?
Lagern Sie zur Übung den entsprechenden Code aus Ihrer Cäsar-Lösung in eine entsprechende Funktion mit der Signatur char rotate(char letter, int key) aus. Diese Funktion nimmt einen Buchstaben und einen Schlüssel, d.h. die Zahl der Verschiebung, entgegen und gibt den verschobenen Buchstaben zurück, wobei Groß- und Kleinschreibung berücksichtigt wird und alle nicht-alphabetischen Zeichen unverändert bleiben (siehe Cäsar).
Alternativ hier kopieren bzw. vergleichen
// Rotates a character in the alphabet n times
char rotate(char letter, int n)
{
// Check if the character is an uppercase or lowercase letter
if (isupper(letter))
{
// Rotate the character within the uppercase alphabet range
return 'A' + (letter - 'A' + n) % ALPHASIZE;
}
else if (islower(letter))
{
// Rotate the character within the lowercase alphabet range
return 'a' + (letter - 'a' + n) % ALPHASIZE;
}
// If the character is not a letter, return it unchanged
return letter;
}In “hello-copy.txt” sollte nach Ausführung des Programms statt Hello World! nun Ifmmp Xpsme! stehen.
Damit haben wir jetzt bereits ein kleines, minimalistisches Programm erstellt, um einfache Textdateien mit der Cäsar-Methode zu verschlüsseln. In den Übungsaufgaben brauchen Sie nun etwas Einarbeitungszeit - lassen Sie sich davon nicht abschrecken! -, aber dafür können Sie dann sogar Audio- und Bild-Dateien manipulieren - und schließlich sogar Daten von einem Speicherkartenabbild wiederherstellen!