Zum Inhalt springen

Wintersemester 2025/26

Wintersemester 2025/26. 9 ECTS: alle Aufgaben, 180 Minuten, 120 Punkte. 6 ECTS: nur A1 bis A6, 120 Minuten, 80 Punkte.

A1: Grundlagen (17 Punkte)

A1a (3 Punkte)

In der Programmierung und im Umgang mit Computersystemen begegnen Sie verschiedenen Dateiformaten, Zahlensystemen und Werkzeugen.

Wie weit können Sie mit 10 Wassergläsern (voll oder leer) maximal zählen, wenn Sie das Binärsystem verwenden und die 0 darstellbar sein soll?
Nennen Sie drei gängige Dateierweiterungen für Bilddateien (durch Leerzeichen getrennt):
Sie haben in C ein Taschenrechner-Programm („calc.c“) geschrieben und in der Ihnen bekannten CS50-Umgebung mit make kompiliert. Es gab keine Fehler. Sie führen das Programm durch Eingabe von calc.c im Terminal aus. Es wird ein Fehler ausgegeben. Was müssen Sie im Terminal stattdessen eingeben, um das Programm auszuführen?

A1b (4 Punkte)

Identifizieren Sie in folgendem C-Programmcode jeweils das am besten passende Beispiel für jeden der aufgelisteten Begriffe. Geben Sie das exakte Zeichen oder die Zeichenfolge aus dem Code an, nicht die Zeilennummer. Falls für einen Begriff kein Beispiel im Programm vorhanden ist, tragen Sie FEHLT ein.

#include <stdio.h>

int main(void)
{
    int anzahl = 10;
    float preis = 12.99;
    char kategorie = 'A';

    printf("Artikel: %d Stueck, Preis: %.2f Euro, Kategorie: %c\n",
           anzahl, preis, kategorie);

    return 0;
}
Ein Datentyp für ganze Zahlen:
Ein Fließkomma-Literal:
Ein Formatcode für Fließkommazahlen:
Ein String-Literal:

A1c (2 Punkte)

Betrachten Sie folgenden C-Codeausschnitt:

int n = 4;
int x = 1;
do
{
    x += x;
    n--;
}
while (n > 0);
Welchen Wert hat x nach Ausführung des Codes?
Welchen Wert hat n nach Ausführung des Codes?

A1d (4 Punkte)

Nennen Sie die Linux-Kommandozeilenbefehle für folgende Aufgaben. Geben Sie nur den Befehlsnamen an (ohne Parameter).

Eine Datei von einem Webserver herunterladen:
Eine Datei in ein anderes Verzeichnis verschieben:
Den Pfad des aktuellen Verzeichnisses anzeigen:
Aufeinanderfolgende gleiche Zeilen in einer sortierten Datei entfernen:

A1e (4 Punkte)

Analysieren Sie folgendes C-Programm:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    FILE *datei = fopen("personen.dat", "r");
    if (datei == NULL)
    {
        return 1;
    }

    char zeile[256];

    fgets(zeile, 256, datei);
    zeile[strlen(zeile) - 1] = '\0';

    int k = 0;
    while (zeile[k] != ',' && zeile[k] != '\0')
    {
        k++;
    }
    zeile[k] = '\0';

    if (strcmp(zeile, "Name") != 0 || strcmp(&zeile[k + 1], "Info") != 0)
    {
        fclose(datei);
        return 1;
    }

    while (fgets(zeile, 256, datei) != NULL)
    {
        k = 0;
        while (zeile[k] != ',' && zeile[k] != '\0')
        {
            k++;
        }
        zeile[k] = '\0';
        int info = atoi(&zeile[k + 1]);

        if (info > 0)
        {
            printf("%s: %d\n", zeile, info);
        }
    }

    fclose(datei);
    return 0;
}
Geben Sie den Inhalt einer gültigen Datei personen.dat mit genau 2 Zeilen an, sodass das Programm exakt folgende Ausgabe erzeugt: “Mars: 1991” (ohne Anführungszeichen):
Wie heißt das Dateiformat, das dieses Programm einliest? (Abkürzung)

A2: Grundlagen der C-Programmierung (14 Punkte)

A2a (2 Punkte)

Analysieren Sie folgende Situation: Ein Programm verwendet eine globale Variable namens counter mit dem Startwert 50. Es gibt eine Funktion berechnung, die zwei Parameter a und b entgegennimmt.

Die Funktion berechnung enthält folgende Anweisungen:

  1. Erhöht die Variable counter um 1.
  2. Legt eine Variable sum auf dem Stack an.
  3. Weist sum den Wert a + b + counter zu.
  4. Gibt sum zurück.

Die Hauptfunktion des Programms arbeitet wie folgt (Pseudocode):

HAUPTFUNKTION:
    x = 8
    y = 12
    ergebnis = AUFRUF berechnung(x, y)
    AUSGABE "Ergebnis: " + ergebnis
    AUSGABE "Counter: " + counter
Welchen Wert hat die Variable ergebnis nach dem Funktionsaufruf?
Welchen Wert hat counter nach dem Funktionsaufruf?

A2b (2 Punkte)

Beantworten Sie folgende Fragen zu C-Bibliotheksfunktionen:

Welche C-Funktion ermittelt die Länge eines Strings? (Nur Funktionsname ohne Klammern)
Mit welcher Funktion können Sie überprüfen, ob zwei Strings identisch sind? (Nur Funktionsname)

A2c (3 Punkte)

Das folgende Programm soll die Summe aller Zahlen von 1 bis n (einschließlich) berechnen, enthält aber drei Fehler:

#include <cs50.h>

int calculate_sum(int n)
{
    int sum = 0;
    for (int i = 1; i < n; i++)
    {
        sum += i;
    }
    return sum;
}

int main(void)
{
    int number = get_int("Geben Sie eine Zahl ein: ");
    int result = calc_sum(number);
    printf("Die Summe von 1 bis %d ist %d\n"; number, result);
    return 0;
}
Finden Sie den logischen Fehler und geben Sie an, was geändert werden muss, um den Fehler zu beheben.
Welche Ausgabe erzeugt das korrigierte Programm für die Eingabe 511? Geben Sie nur den Wert an, den result enthält.

A2d (7 Punkte)

Das Programm statistik.c verarbeitet ein Integer-Array zahlen mit den Werten und gibt Minimum, Maximum und Durchschnitt aus.

Erwartete Ausgabe:

Min: 3
Max: 31
Avg: 13.75

Unten stehen 15 nummerierte Codezeilen. Elf davon ergeben zusammen das korrekte Programm – die restlichen vier gehören nicht dazu. Geben Sie die Nummern der korrekten Zeilen in der richtigen Reihenfolge an.

 1: sum += zahlen[i]; }
 2: printf("Min: %d\n", min);
 3: int min = zahlen[0], max = zahlen[0]; float sum = 0;
 4: for (int i = 1; i <= 8; i++) {
 5: int main(void) {
 6: printf("Avg: %.2f\n", sum / 8);
 7: for (int i = 0; i < 8; i++) {
 8: return 0; }
 9: printf("Avg: %d\n", sum / 8);
10: #include <stdio.h>
11: if (zahlen[i] < min) min = zahlen[i]; if (zahlen[i] > max) max = zahlen[i];
12: int zahlen[] = {14, 7, 23, 3, 18, 9, 31, 5};
13: printf("Max: %d\n", max);
14: int sum = 0; int min = 0, max = 0;
15: int *zahlen; zahlen = [14, 7, 23, 3, 18, 9, 31, 5];
Reihenfolge der korrekten Zeilen (kommasepariert mit Leerzeichen, z.B. 5, 12, 10, …):

A3: IBAN validieren (15 Punkte)

Beim Bezahlen Ihrer Semestergebühren haben Sie eine sogenannte IBAN (International Bank Account Number) verwendet. Dabei handelt es sich um eine standardisierte Notation für Kontonummern.

Diese ist für Deutsche Konten folgendermaßen aufgebaut:

  • 2-stelliger Ländercode bestehend aus Großbuchstaben (in Deutschland “DE”)
  • 2-stellige Prüfziffer
  • Eine 18 stellige Kontoidentifikation, bestehend aus Bankleitzahl und Kontonummer

Ihre Aufgabe ist es nun, ein Programm zu schreiben, das eine solche IBAN als Kommandozeilenparameter entgegennimmt und auf Gültigkeit prüft. Um die Komplexität der Aufgabe überschaubar zu halten, berücksichtigen wir ausschließlich deutsche Kontonummern.

Den unvollständigen Quellcode des Programms finden Sie im Verzeichnis exam-files/iban in Ihrem Home-Verzeichnis. Für diese Aufgabe steht Ihnen check50 zur Verfügung. Um check50 zu verwenden, führen Sie folgenden Befehl in dem Verzeichnis aus, in dem sich die Datei iban.c befindet:

check50 checks/iban

Zuvor sollten Sie Ihr Programm kompilieren.

A3a (5 Punkte)

Prüfen Sie zunächst die Eingaben des Nutzers auf Vollständigkeit:

  • Gibt der Nutzer keinen oder mehr als einen Kommandozeilenparameter an, geben Sie die Nachricht: Usage: ./iban <iban> aus.
  • Wenn die IBAN zu kurz oder zu lang ist, geben Sie die Nachricht: IBAN too long or too short aus.

Fügen Sie jeweils ein Newline-Escapezeichen hinzu und beenden Sie das Programm mit dem Rückgabewert 1.

Kopieren Sie den gesamten Quellcode von iban.c in dieses Feld.

A3b (5 Punkte)

Prüfen Sie nun die Syntax der eingegebenen IBAN.

  • Wenn die ersten 2 Stellen nicht dem Ländercode DE entsprechen, geben Sie die Nachricht: Not a german IBAN aus.
  • Wenn die IBAN ab der dritten Stelle (index 2) andere Zeichen als Ziffern (0-9) enthält, geben Sie die Nachricht: IBAN contains invalid digits aus.

Fügen Sie jeweils ein Newline-Escapezeichen hinzu und beenden Sie das Programm mit dem Rückgabewert 1.

Kopieren Sie den gesamten Quellcode von iban.c in dieses Feld.

A3c (5 Punkte)

Ermitteln Sie nun im letzten Schritt, ob die eingegebene IBAN gültig ist. Vervollständigen Sie dazu die Funktion validate_iban. Diese nimmt eine IBAN als String entgegen, berechnet die Prüfsumme und gibt 1 im Fall einer gültigen IBAN und 0 im Fall einer ungültigen IBAN zurück.

Geben Sie anschließend im Fall einer gültigen IBAN den String IBAN is valid aus, ansonsten IBAN is invalid und beenden Sie das Programm mit dem Rückgabewert 0.

Eine IBAN wird folgendermaßen berechnet:

Zunächst wird das Länderkennzeichen “DE” als Zahlenwert (D=13, E=14) an die Ziffern der Kontoidentifikation angehängt. Dadurch entsteht eine 22 Ziffern lange Zahl. An diese Zahl werden dann die Prüfziffern angehängt, wodurch eine insgesamt 24 Ziffern lange Zahl entsteht. Anschließend wird diese Zahl dann mit Rest durch 97 geteilt. Wenn das Ergebnis 1 ist, ist die IBAN gültig, andernfalls nicht.

Hinweise: Sie benötigen zum Teilen mit Rest den Modulo-Operator (% auf der Tastatur). Die gesamte Zahl ist zu groß für einen Integer in C. Gehen Sie daher folgendermaßen vor: Starten Sie mit rest = 0. Gehen Sie dann Ziffer für Ziffer vor, indem sie den Rest mit 10 multiplizieren, die nächste Ziffer addieren und anschließend Modulo 97 rechnen, um den Rest neu zu berechnen. Oder um es noch kompakter darzustellen: neuer_rest = (alter_rest * 10 + ziffer) % 97

Kopieren Sie den gesamten Quellcode von iban.c in dieses Feld.

A4: ID3 Tags (13 Punkte)

Das Audioformat MP3 verfügte anfangs über keine Möglichkeit, Informationen über den Inhalt einer Datei zu speichern. Im Jahr 1997 erfand Eric Kemp mit ID3 ein einfaches Metadatenformat, das aus einem 128 Byte langen Tag (also einer Datenstruktur mit einem definiertem Format) besteht, der am Ende einer Audiodatei angehängt wird. In diesem Tag werden Informationen wie Interpret, Album und Songname gespeichert.

Die Dateien für diese Aufgabe finden Sie im Verzeichnis exam-files/id3 in Ihrem Home-Verzeichnis.

A4a (7 Punkte)

Vervollständigen Sie das Programm id3v1.c, um den ID3-Tag aus dem Song file.mp3 zu extrahieren. Berücksichtigen Sie dazu die folgenden Informationen.

In der ursprünglichen ID3-Version 1.0 war ein ID3-Tag wie folgt spezifiziert:

MP3 ID3 Tag 1.0 -- 128 bytes
(c) 1996, 1997 NamkraD (Eric Kemp)
Seek end of file, then -128, if first 3 bytes='TAG' then it has info layed
out as follows...

0..2 == 'TAG' (3 Bytes)
3..32 == Song Name (30 bytes)
33..62 == Artist (30 Bytes)
63..92 == Album Name (30 Bytes)
93..96 == Year (4 Bytes)
97..126 == Comment (30 Bytes)
127 == 1 Byte Song Genre Identifier
(get the listing of song Genre Types to know what byte = what)

Definieren Sie in id3v1.c eine struct, die dem obigen Schema entspricht, lesen Sie die Informationen aus der Datei file.mp3 in die struct ein und geben Sie die struct-Attribute mit den bereits vorhandenen printf-Anweisungen aus.

Hinweis: Für das Genre-Byte können Sie den Datentyp uint8_t verwenden. Für die restlichen Felder eignet sich char.

Kopieren Sie den gesamten Inhalt von id3v1.c in dieses Feld.

Ein Freund hat die Aufgabe mit den vorgegebenen printf-Anweisungen gelöst, beobachtet jedoch folgende Ausgabe:

...
Year: 1987:)
Comment: :)
...
Wie zu sehen ist, wird der Inhalt des Kommentar-Felds auch beim Jahr ausgegeben. Warum?

A4b (6 Punkte)

ID3 wurde von M. Nilsson zu ID3v2 weiterentwickelt. In ID3v2 können komplexere Daten abgelegt werden, unter anderem auch ein Album-Cover-Bild. Ihre Aufgabe ist es im Folgenden, das Album-Cover-Bild des Songs zu extrahieren.

Ein ID3v2-Tag befindet sich am Anfang einer Datei und besteht aus einem ID3v2-Header-Tag, in dem einer oder mehrere ID3v2-Frames eingebettet sind. Diese ID3v2-Frames enthalten jeweils einen Header, der die Art der Metadaten beschreibt, sowie die Metadaten selbst.

Im vorliegenden Fall (Datei: id3v2.c) ist bereits Code vorhanden, der den ID3v2-Header und den Attached-Picture (APIC) Frame einliest. Der APIC-Frame wird hierbei in den Heap eingelesen und über den Pointer frame_data referenziert. Ihre Aufgabe ist es nun, die Metadaten des APIC-Headers zu überspringen und anschließend das Album-Cover-Bild in der Datei cover.png abzuspeichern.

Ein APIC-Frame sieht folgendermaßen aus:

Text encoding   $xx (1 Byte)
MIME type       <text string>'\0' (String, terminiert durch Nullbyte)
Picture type    $xx (1 Byte)
Description     <text string>'\0' (String, terminiert durch Nullbyte)
Picture data    <binary data>

Hinweis: Sollte es Ihnen nicht gelingen, den korrekten Offset zu ermitteln, können Sie stattdessen den Wert 42 verwenden.

Kopieren Sie den gesamten Inhalt von id3v2.c in dieses Feld.

A5: Datenstrukturen und Algorithmen (14 Punkte)

A5a (3 Punkte)

Ein unsortiertes Array enthält folgende Elemente 5,4,2,1,35, 4, 2, 1, 3. Sie beobachten die ersten zwei Durchläufe der äußeren Schleife eines Sortieralgorithmus, der auf dieses Array angewendet wird.

Nach Durchlauf 1: [4, 2, 1, 3, 5]

Nach Durchlauf 2: [2, 1, 3, 4, 5]

Welcher der beiden Algorithmen – Bubble Sort oder Selection Sort – hat diese Zustände produziert? Auf welche Beobachtung stützen Sie Ihre Entscheidung?

Geben Sie in der ersten Zeile den Algorithmus an, ab der zweiten Zeile Ihre Begründung.

A5b (6 Punkte)

Zur Erinnerung: Ein Trie kann platzsparend mit folgender Datenstruktur implementiert werden:

struct trie_node {
  char buchstabe;
  char* wort; // Zeiger auf das gespeicherte Wort (falls vorhanden; sonst NULL)
  struct trie_node* nachbar;
  struct trie_node* kind;
};

Mit dieser Datenstruktur wurde ein Trie für Abkürzungen aufgebaut, der bereits folgende Einträge enthält:

  • HTML: Hypertext Markup Language
  • HTTP: Hypertext Transfer Protocol

Die Speicherrepräsentation sieht wie folgt aus (Adressen sind vierstellig):

// Root-Knoten (zeigt auf den ersten Buchstaben)
struct trie_node* root = 0x1000;

0x1000: ['H', NULL, NULL, 0x2000]
0x2000: ['T', NULL, NULL, 0x3000]
0x3000: ['T', NULL, 0x7000, 0x5000]
0x5000: ['P', Zeiger auf "Hypertext Transfer Protocol", NULL, NULL]
0x7000: ['M', NULL, NULL, 0x8000]
0x8000: ['L', Zeiger auf "Hypertext Markup Language", NULL, NULL]

Als nächstes soll der Eintrag HTTPS: Hypertext Transfer Protocol Secure in den Trie eingefügt werden. Eine Kollegin sagt: Moment mal, der aktuelle Trie enthält nur 4-Buchstaben-Abkürzungen, aber jetzt fügen wir eine 5-Buchstaben-Abkürzung ein. Das bedeutet, wir müssen die vorherigen Inhalte aktualisieren und ein spezielles End-of-Entry-Zeichen anhängen, sonst wird der Trie den String „Hypertext Transfer Protocol“ zurückgeben, wenn wir später die neue Abkürzung „HTTPS“ nachschlagen.

Ist die Aussage Ihrer Kollegin korrekt? Antworten Sie in der ersten Zeile mit Ja oder Nein. Begründen Sie ab der zweiten Zeile.

Erweitern Sie nun die Trie-Struktur um den neuen Eintrag HTTPS: Hypertext Transfer Protocol Secure. Verwenden Sie die Adresse 0x9000 für den neuen Knoten. Vervollständigen Sie die Felder der betroffenen Knoten in der Notation [buchstabe, wort, nachbar, kind]:

Geänderter Knoten:
0x5000: [___, ___, ___, ___]

Neuer Knoten:
0x9000: [___, ___, ___, ___]
Geben Sie die vollständigen Inhalte der beiden Knoten an.

Erweitern Sie die Trie-Struktur zusätzlich um den Eintrag JSON: JavaScript Object Notation. Verwenden Sie die Adressen 0xA000 bis 0xD000 für die neuen Knoten. Orientieren Sie sich dabei am vorgegebenen Antwortschema und ändern Sie nur die dort angegebenen Knoten.

Geänderter Knoten:
0x1000: [___, ___, ___, ___]

Neue Knoten:
0xA000: [___, ___, ___, ___]
0xB000: [___, ___, ___, ___]
0xC000: [___, ___, ___, ___]
0xD000: [___, ___, ___, ___]
Geben Sie die vollständigen Inhalte aller Knoten an.

A5c (5 Punkte)

Unten stehen 11 nummerierte Codezeilen für ein C-Programm, das mit malloc Speicher für vier Ganzzahlen allokiert, die Werte 40, 30, 20, 10 zuweist und sie in umgekehrter Reihenfolge ausgibt. Acht Zeilen ergeben das korrekte Programm – die restlichen drei gehören nicht dazu. Zwei Zeilen enthalten Lücken (LUECKE_1 und LUECKE_2), die jeweils das Argument für malloc darstellen. Ziffer 8 fügt die zwei angegebenen Zeilen hintereinander ein.

Beide Lücken enthalten denselben Ausdruck; relevant ist nur die korrekte malloc-Zeile.

Erwartete Ausgabe:

Wert 0: 10
Wert 1: 20
Wert 2: 30
Wert 3: 40
 1: return 0; }
 2: printf("Wert %d: %d\n", i, arr[i]); }
 3: free(arr);
 4: int *arr = malloc(LUECKE_1);
 5: for (int i = 0; i < 4; i++) {
 6: arr[0] = 40; arr[1] = 30; arr[2] = 20; arr[3] = 10;
 7: int main(void) {
 8: #include <stdio.h>
    #include <stdlib.h>
 9: printf("Wert %d: %d\n", 3 - i, arr[i]); }
10: for (int i = 3; i >= 0; i--) {
11: int arr[4] = malloc(LUECKE_2);
Reihenfolge der korrekten Zeilen (kommasepariert, z.B. 8, 7, 4, …):
Was muss in die Lücke eingesetzt werden, damit die korrekte malloc-Zeile funktioniert?

A6: Zeiger und Datenstrukturen (7 Punkte)

A6a (4 Punkte)

Ein C-Programm soll die Werte zweier Variablen mithilfe einer Funktion vertauschen. Unten stehen 16 nummerierte Codezeilen. Zehn davon ergeben das korrekte Programm – die restlichen sechs gehören nicht dazu. Zwei Zeilen enthalten Lücken: LUECKE_1 hat die Struktur ??? = ??? und LUECKE_2 besteht aus den Argumenten des Funktionsaufrufs.

 1: int temp = a;
 2: #include <stdio.h>
 3: void vertausche(int *a, int *b) {
 4: return a; }
 5: int x = 15; int y = 25;
 6: LUECKE_1;
 7: vertausche(x, y);
 8: int main(void) {
 9: *b = temp; }
10: int temp = *a;
11: a = b;
12: printf("Nach dem Tausch: x = %d, y = %d\n", x, y);
13: b = temp;
14: int vertausche(int a, int b) {
15: vertausche(LUECKE_2);
16: return 0; }
Reihenfolge der korrekten Zeilen (kommasepariert, z.B. 2, 3, 10, …):
Was muss in LUECKE_1 bzw. LUECKE_2 stehen, damit die jeweilige Zeile korrekt ist?

A6b (3 Punkte)

Eine Programmiererin oder ein Programmierer hat versucht, eine Funktion zum Löschen eines Elements aus einer einfach verketteten Liste zu implementieren. Die Implementierung enthält jedoch einen Fehler, der dazu führt, dass das Löschen des ersten Elements nicht korrekt funktioniert.

Hier ist die fehlerhafte Implementierung:

#include <stdbool.h>
#include <stdlib.h>

typedef struct sllnode {
    int value;
    struct sllnode *next;
} sllnode;

// TODO: Fehler beim Löschen des ersten Elements beheben
bool delete_element(sllnode* head, int value) {
    if (head == NULL) {
        return false; // Liste ist leer
    }

    // Erstes Element löschen
    if (head->value == value) {
        sllnode* temp = head;
        head = head->next;
        free(temp);
        return true;
    }

    // Anderes Element löschen
    sllnode* current = head;
    while (current->next != NULL) {
        if (current->next->value == value) {
            sllnode* temp = current->next;
            current->next = temp->next;
            free(temp);
            return true;
        }
        current = current->next;
    }
    return false; // Element nicht gefunden
}
Beheben Sie den Fehler, indem Sie einen Doppelpointer (sllnode**) verwenden. Sie müssen dazu auch die Signatur der Funktion delete_element verändern. Fügen Sie den Code der korrigierten Funktion delete_element hier ein:

B1: Python und OOP (20 Punkte)

B1a (4 Punkte)

Für ein Projekt haben Sie einen Mikrocontroller mit verschiedenen Sensoren ausgestattet, um deren Werte zu erfassen. Die gesammelten Messdaten sollen in Python ausgewertet werden.

Die Klasse Measurement (Datei oop.py im Verzeichnis exam-files/python_oop) dient als allgemeine Vorlage für Sensormessungen. Der Code unten zeigt die Klasse mit zwei Lücken. Füllen Sie diese aus.

class Measurement:
    def __init__(self, sensor_id, value, time):
        self.sensor_id = sensor_id
        self.value = value
        self.time = time

    def __str__(self):
        ___Lücke_1___

___Lücke_2___

Lücke 1 ist die Implementierung der __str__-Methode (eine Zeile genügt). In Lücke 2 erstellen Sie ein Objekt mit den folgenden Attributwerten und geben es mit print() aus:

value = 22.3
time = "2025-07-17T08:15:00"
sensor_id = "T1"

Die Ausgabe von print(obj) soll so aussehen:

2025-07-17 08:15:00 Sensor T1: 22.3
Lücke 1: Implementierung von __str__ (eine Zeile):
Lücke 2: Objekt erstellen und mit print() ausgeben:

B1b (6 Punkte)

Nutzen Sie die erweiterte Klasse Measurement aus der Datei subclasses.py (Verzeichnis exam-files/python_oop) als Basisklasse. Der Code unten zeigt eine Subklasse mit drei Lücken.

  • Lücke 1 enthält die Klassendefinitionszeile mit Vererbung für die Klasse TemperatureMeasurement.
  • Lücke 2 enthält eine Methode is_valid(). Diese gibt True zurück, wenn der Messwert im gültigen Bereich liegt (Temperatur: –20 bis +60), sonst False.
  • Lücke 3 ist eine Property unit. Diese gibt die passende Einheit zurück (bei Temperatur: “°C”).
___Lücke_1___
    measurement_type = "temperature"

    ___Lücke_2___

    ___Lücke_3___
Tragen Sie hier den Inhalte von Lücke 1 ein:
Tragen Sie hier den Inhalte von Lücke 2 ein:
Tragen Sie hier den Inhalte von Lücke 3 ein:

B1c (6 Punkte)

Für diese Aufgabe benötigen Sie die Dateien read_measurements.py und measurements.json. Beide Dateien befinden sich ebenfalls im Verzeichnis exam-files/python_oop.

Implementieren Sie die Funktion load_measurements_from_json, die die JSON-Datei einliest und für jeden Eintrag in der JSON-Datei das passende Messobjekt erstellt. Das heißt, es soll je nach Typ (“temperature” oder “co2”) eine Instanz der Klasse TemperatureMeasurement oder CarbonDioxideMeasurement erzeugt werden. Sie können davon ausgehen, dass die Werte in der JSON-Datei in °C (Grad Celsius) bzw. ppm (parts per million) vorliegen.

Kopieren Sie den gesamten Inhalt von read_measurements.py in dieses Feld.

In main() geben Sie die erzeugten Objekte anschließend sortiert nach ihrem Zeitstempel aus. Zur Erinnerung: sorted() akzeptiert einen key-Parameter, der bestimmt, wonach sortiert wird:

sorted(["Hund", "Katze", "Maus"], key=lambda x: len(x))

Die Zeitstempel in der JSON-Datei sind bereits so formatiert, dass eine Sortierung als String ausreicht.

Dabei sollen die Attributwerte in folgender Reihenfolge ausgeben werden: time, sensor_id, value, unit.

Rufen Sie zudem – abhängig vom Typ des Messobjekts – die passende Methode auf: bei Temperaturmessungen convert_celsius_to_fahrenheit() und bei CO₂-Messungen get_air_status(). Hängen Sie den Rückgabewert dieser Methoden jeweils ans Ende der Zeile an.

Kopieren Sie den gesamten Inhalt von read_measurements.py in dieses Feld.

B1d (4 Punkte)

Betrachten Sie die folgende Python-Interpreter-Sitzung:

>>> import random
>>> import string
>>> string.ascii_letters
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> random.choice(string.ascii_letters)
'k'
>>> random.choice(string.ascii_letters)
'T'
>>> "".join([random.choice(string.ascii_letters) for _ in range(6)])
'SRTGlv'
Wie nennt man das Python-Konstrukt [... for ... in ...], das hier als Argument von join() verwendet wird?

Schreiben Sie Python-Code, der ohne dieses Konzept einen zufälligen Code aus 6 Buchstaben erzeugt und in der Variable code speichert. Verwenden Sie stattdessen eine for-Schleife, um nach und nach zufällige Zeichen an code anzuhängen.

Geben Sie den Code mit for-Schleife hier ein:

B2: Web-Entwicklung: Link-Shortener (20 Punkte)

In den folgenden Teilaufgaben entwickeln Sie einen Link Shortener mit Flask. Sie können dort eine URL eingeben und erhalten einen kurzen Code, über den die URL später wieder aufgerufen werden kann. Die Zuordnungen von URLs und Codes werden vom Server in einer JSON-Datei gespeichert.

Das Skelett der Anwendung finden Sie im Verzeichnis exam-files/shortener. Öffnen Sie die Datei app.py und die Templates im Ordner templates/ – dort finden Sie vorbereitete Funktionen und Routen mit TODO-Kommentaren, die Sie Schritt für Schritt vervollständigen.

Damit Code-Änderungen vom Flask-Server automatisch berücksichtigt werden, müssen Sie den Server wie folgt starten:

flask --debug run

Um die Anwendung im Browser zu öffnen, klicken Sie bei gedrückter Strg-Taste auf die URL (Running on …), die Flask nach dem Start anzeigt. Um Flask zu beenden, verwenden Sie die Tastenkombination Strg-C.

B2a (4 Punkte)

Vervollständigen Sie das Template index.html. Erstellen Sie ein HTML-Formular mit:

  • Methode POST, Ziel beim Submit /shorten
  • Eingabefeld für die URL (Name: url)
  • Eingabefeld für den optionalen Kurzcode (Name: code)
  • Absende-Button mit Text Shorten!
Geben Sie den HTML-Code für das Formular hier ein (nur das Formular; nicht die ganze Datei):

B2b (6 Punkte)

Vervollständigen Sie in app.py die Funktion write_links(links) sowie die Route /shorten.

write_links ist das Gegenstück zu read_links() (die bereits fertig in app.py steht): Die Funktion soll das übergebene Dictionary links als JSON in die Datei schreiben.

Tragen Sie hier den Code der Funktion write_links(links) ein (nicht die ganze Datei):

Route /shorten: Die Route empfängt einen POST-Request. Implementieren Sie die folgenden Schritte:

  1. Die Formularfelder url und code aus dem Request auslesen (mit request.form[...])
  2. Falls code leer ist: einen zufälligen 6-Buchstaben-Code erzeugen (siehe Hilfskommentar am Ende von app.py)
  3. Die bestehenden Links mit read_links() laden, den neuen Eintrag code → url hinzufügen und mit write_links() speichern
  4. Das Template result.html rendern und dabei die Variablen code und url übergeben
Tragen Sie hier den Code der Route /shorten ein (nicht die ganze Datei):

Tipp zum Testen: Sie können die Route auch ohne funktionierendes Formular testen. Der Befehl

curl -X POST -d "url=https://example.com&code=test1" http://...

sendet eine HTTP-POST-Anfrage mit Formulardaten an die URL am Ende des curl-Aufrufs. An welche Ziel-IP-Adresse und welchen Ziel-Port müssen Sie diese Anfrage senden, wenn Sie Flask mit flask --debug run (ohne zusätzliche Parameter) starten? Geben Sie die vollständige URL inklusive Pfad an.

Vollständige URL für den curl-Befehl:

B2c (5 Punkte)

Redirect-Route: Schreiben Sie eine neue Route in app.py, die unter /CODE erreichbar ist, wobei CODE ein variabler Kurzcode ist (z.B. /abc123 oder /test1). Die Route soll:

  1. Die gespeicherten Links mit read_links() laden
  2. Falls der Kurzcode im Dictionary existiert: mit redirect() zur gespeicherten URL weiterleiten
  3. Falls nicht: "Link nicht gefunden" mit Statuscode 404 zurückgeben
Tragen Sie hier Ihren Code inklusive Decorator ein (nicht das ganze Python-Programm):

Tipp zum Testen: Legen Sie über das Formular (oder per curl) einen Link mit dem Code infdoc an, der auf http://127.0.0.1:8080/infeinf-docs.html verweist. Wenn Ihre Redirect-Route funktioniert, sollte der Aufruf von /infdoc im Browser zur Dokumentationsseite weiterleiten.

B2d (5 Punkte)

Jinja-Template: Schreiben Sie das vollständige Template links.html, sodass alle gespeicherten Links als Tabellenzeilen angezeigt werden. Die Variable links wird als Dictionary übergeben (Kurzcode → URL). Jede Zeile soll den Code und die URL (als klickbaren Link) enthalten.

Tragen Sie hier den gesamten Inhalt der Datei links.html ein:

Diese Seite als Markdown: ansehen herunterladen