Projekt 1 : "Geheime Nachrichten" (Teil 3)

Website: Lerne ELEKTROTECHNIK und PROGRAMMIEREN
Kurs: Kurs zum Buch "Lerne Programmieren mit Projekten: C++"
Buch: Projekt 1 : "Geheime Nachrichten" (Teil 3)
Gedruckt von: Gast
Datum: Friday, 29. March 2024, 11:51

Beschreibung



1. Worum geht es?

In den vorangegangenen Abschnitten haben wir die Grundstruktur für das Programm entwickelt:  

  • Wir können auswählen, ob ver- oder entschlüsselt werden soll.
  • Wir können Text einlesen und zeichenweise abarbeiten.
  • Wir können den Text in Zahlen (ASCII-Codes) verwandeln.

In diesem Abschnitt wird es nun darum gehen, ein Verfahren zu entwickeln, mit dem der zu verschlüsselnde Text für Dritte unleserlich gemacht werden kann. 

Ein einfaches Verfahren zur Verschlüsselung von Text ist eine angeblich auf Julius Cäsar zurückgehende Methode. Hierbei wird jeder Buchstabe durch eine festgelegte Anzahl von Stellen (die Geheim-Zahl) nach vorne oder hinten im Alphabet verschoben. Wenn der Empfänger der verschlüsselten Nachricht diese Anzahl kennt, kann er den Text durch verschieben in die Gegenrichtung wieder leserlich machen.

Hintergründe zur Cäsar-Methode und zur Umsetzung in Programm-Code findest du im Lernheft zum Kurs.


2. Die Cäsar-Verschlüsselung

Bei der Cäsar-Verschlüsselung wird jeder Buchstabe durch eine festgelegte Anzahl von Stellen (die Geheim-Zahl im Alphabet verschoben. Wenn der Empfänger der verschlüsselten Nachricht diese Anzahl kennt, kann er den Text durch verschieben in die Gegenrichtung wieder leserlich machen.

Ein erster (etwas naiver) Ansatz zur Programmierung dieses Verfahrens könnte sein, einfach einen festen Wert auf den ASCII-Code jedes Zeichens aufzuschlagen wie im folgenden  Code gezeigt.  


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

int main()
{
    string output, input = "!AByz~";
    int caesar = 5; // Geheime Code-Zahl 

    int cnt = 0;
    while(cnt<input.size())
    {
        // Buchstabe zu Zahl umwandeln
        int ascii = (int)input[cnt]; 

        // Zahl um festen Wert verschieben
        int new_ascii = ascii + caesar;

        // Zahl zu Buchstabe rückwandeln
        char code = (char)new_ascii; 

        // Neuen Buchstaben an Ergebnis anhängen
        output.push_back(code); 

        cnt++; 
    }
    cout << "Nachricht: " << output << endl;

    return 0;
}

Für die Buchstaben ist auch ohne ASCII-Tabelle zu erkennen, dass die Verschiebung um ein Zeichen im Alphabet funktioniert hat. Allerdings sind am Ende der Ausgabe merkwürdige Werte zu erkennen, die nicht aus einem, sondern aus mehreren Zeichen bestehen.

Der Grund hierfür ist, dass das letzte druckbare Zeichen "!" den ASCII-Code 126 hat und eine Verschiebung um den Wert 5 dazu führt, dass der maximal zulässige Zahlenwert von 127 in der ASCII-Tabelle überschritten wird, da 126+5=131 ergibt. Wir müssen also im nächsten Beispiel noch ein wenig an unserem Code weiterarbeiten, um dieses Problem zu lösen.

3. Zyklische Verschiebung mit MODULO

Im letzten Abschnitt haben wir gesehen, dass eine simple Addition der Geheim-Zahl auf die jeweiligen ASCII-Codes nicht immer funktionieren würde: Sobald die Zahl 126 überschritten oder die Zahl 32 unterschritten werden, verlassen wir den Bereich der druckbaren Zeichen oder sogar den zulässigen Wertebereich der ASCII-Tabelle.

Was wir also brauchen, ist eine zyklische Verschiebung, die nach dem Überschreiten des zulässigen Maximalwerts wieder von vorne beginnt, d.h. aus dem letzten druckbaren Zeichen"~" mit dem Code 126 würde bei einer Verschiebung um den Wert 1 das Leerzeichen mit dem Wert 32.

Eine mögliche Lösung hierfür bietet der Modulo-Operator %: Hiermit wird der Rest einer Division zurückgegeben, d.h. der Befehl 9 % 2 ergibt den Wert 1, da die Rechenoperation 9/2 das Ergebnis "4 Rest 1" liefert. Die Modulo-Operation 9 % 3 liefert stattdessen den Wert 0, da die Zahl 9 ohne Rest durch 3 teilbar ist. 

Im nachstehenden Code sind einige Beispiele zu sehen, in denen der Modulo-Operator genutzt wird, um Zahlen auf einen eingeschränkten Bereich abzubilden. 

#include <iostream>
using namespace std;

int main()
{

    cout << "Abbilden auf Zahlenraum 0-9" << endl;
    // Bereich umfasst 10 Zahlen
    cout << 9 % 10 << endl;
    cout << 10 % 10 << endl;
    cout << 11 % 10 << endl;

    cout << "Abbilden auf Zahlenraum 3-9" << endl;
    // Bereich umfasst 7 Zahlen
    int first = 3, last=9;
    int mod = (last-first+1);
    cout << (9 - first) % mod + first << endl;
    cout << (10 - first) % mod + first << endl;
    cout << (11- first) % mod + first << endl;
    
    return 0;
}

4. Das fertige Programm

Nachdem wir nun wissen, wie die Cäsar-Methode und die zyklische Modulo-Verschiebung funktionieren, kann die Verschlüsselung im Projekt fertiggestellt werden. Am Anfang der main()-Funktion wird in dieser finalen Version noch ein wenig ASCII-Art in Form eines Schlosses auszugeben. Warum? Weil es nett aussieht! 😉

Zeile 1 : 'v' oder 'e'
Zeile 2 : Text zum Verschlüsseln
#include <iostream>
#include <string>

using namespace std;

int main()
{
    cout << "     .--------. " << endl;
    cout << "    / .------. \\ " << endl;
    cout << "   / /        \\ \\ " << endl;
    cout << "   | |        | |" << endl;
    cout << "  _| |________| |_" << endl;
    cout << ".' |_|        |_| '." << endl;
    cout << "'._____ ____ _____.'" << endl;
    cout << "|     .'____'.     |" << endl;
    cout << "'.__.'.'    '.'.__.'" << endl;
    cout << "'.__  |GEHEIM|  __.'" << endl;
    cout << "|   '.'.____.'.'   |" << endl;
    cout << "'.____'.____.'____.'" << endl;
    cout << "'.________________.'" << endl;
    cout << endl;

    // TEIL 1: Ver- oder Entschlüsselung auswählen
    string input;
    cout << "Ver- oder Entschlüsseln? (v/e): " << endl;
    getline(cin, input);

    bool encode = false; // später ENTschlüsseln
    if (input == "v")
    {
        cout << "Text zum VERschlüsseln: " << endl;
        encode = true;
    }
    else if (input == "e")
    {
        cout << "Text zum ENTschlüsseln: " << endl;
    }
    else
    {
        cout << "Ungültige Eingabe!" << endl;
        exit(EXIT_FAILURE); // Fehlercode melden
    }

    // TEIL 2: Textzeichen nacheinander verarbeiten
    string output;
    getline(cin, input);
    int caesar = 1; // Geheime Code-Zahl

    int cnt = 0;
    while (cnt < input.size())
    {
        // TEIL 3: Erzeugen von Geheim-Nachrichten
        // Buchstabe zu Zahl umwandeln
        int ascii = (int)input[cnt];

        // Zahl um festen Wert verschieben ...
        int new_ascii;
        if (encode == true) // Verschlüsseln
        {
            new_ascii = (ascii + caesar - 32) % 95 + 32;
        }
        else // Entschlüsseln
        {
            // FEHLT NOCH
        }

        // Zahl zu Buchstabe rückwandeln
        char code = (char)new_ascii;

        // Neuen Buchstaben an Ergebnis anhängen
        output.push_back(code);

        cnt++;
    }
    cout << "Nachricht: " << output << endl;

    return EXIT_SUCCESS; // wahlweise auch 0
}

Herzlichen Glückwunsch, denn damit ist die Entwicklung des Verschlüsselungs-Programms beendet und du hast dein erstes Projekt erfolgreich abgeschlossen!

Im nächsten Abschnitt findest du einige Aufgaben, mit denen du den Funktionsumfang des Programms erweitern (z.B. die Entschlüsselung programmieren) und deine neu gelernten Fähigkeiten in der Praxis testen kannst. Viel Spaß dabei!