Frage zu einfachem Verschlüsselungslgorithmus



  • @ asc
    Das mit den ' habe ich nicht beachtet stimmt 😉
    Aber ich habe noch nicht verstanden, wie du mit Hilfe der Ascii Werte den riesigen Switch Block auflösen willst.

    Könntest du mir das bitte genau erklären?

    Ich habe das Programm jetzt mal ein bisschen angepasst, aber ich bekomme zwei Fehler meldungen:
    error: expected primary-expression before "char"
    error: expected ';' before "char"

    Könntet ihr mir sagen, wie ich das beheben kann?

    Hier mal der derzeitige Code:

    #include <iostream>
    using namespace std;
    
    int main()
    {
        cout << "Jans Verschluesselungs-Algorithmus!" << endl;
    	cout << "Bitte geben Sie ein Wort ein!" << endl;
    	char 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z';
        char wort;
    	cin >> wort;
    	cout << endl << "Das Wort lautet verschluesselt: \"" << endl;
    	Switchstart:
    	switch(wort){
    	case 'a': cout << "1 ,";
    	case 'b': cout << "2 ,";
    	case 'c': cout << "3 ,";
    	case 'd': cout << "4 ,";
    	case 'e': cout << "5 ,";
    	case 'f': cout << "6 ,";
    	case 'g': cout << "7 ,";
    	case 'h': cout << "8 ,";
    	case 'i': cout << "9 ,";
    	case 'j': cout << "10 ,";
    	case 'k': cout << "11 ,";
    	case 'l': cout << "12 ,";
    	case 'm': cout << "13 ,";
    	case 'n': cout << "14 ,";
    	case 'o': cout << "15 ,";
    	case 'p': cout << "16 ,";
    	case 'q': cout << "17 ,";
    	case 'r': cout << "18 ,";
    	case 's': cout << "19 ,";
    	case 't': cout << "20 ,";
    	case 'u': cout << "21 ,";
    	case 'v': cout << "22 ,";
    	case 'w': cout << "23 ,";
    	case 'x': cout << "24 ,";
    	case 'y': cout << "25 ,";
    	case 'z': cout << "26 ,";
    	case 'A': cout << "27 ,";
    	case 'B': cout << "28 ,";
    	case 'C': cout << "29 ,";
    	case 'D': cout << "30 ,";
    	case 'E': cout << "31 ,";
    	case 'F': cout << "32 ,";
    	case 'G': cout << "33 ,";
    	case 'H': cout << "34 ,";
    	case 'I': cout << "35 ,";
    	case 'J': cout << "36 ,";
    	case 'K': cout << "37 ,";
    	case 'L': cout << "38 ,";
    	case 'M': cout << "39 ,";
    	case 'N': cout << "40 ,";
    	case 'O': cout << "41 ,";
    	case 'P': cout << "42 ,";
    	case 'Q': cout << "43 ,";
    	case 'R': cout << "44 ,";
    	case 'S': cout << "45 ,";
    	case 'T': cout << "46 ,";
    	case 'U': cout << "47 ,";
    	case 'V': cout << "48 ,";
    	case 'W': cout << "49 ,";
    	case 'X': cout << "50 ,";
    	case 'Y': cout << "51 ,";
    	case 'Z': cout << "52 ,";
        goto Switchstart;
    	}
        cout << " \"" << endl;
    	return 0;
    }
    

    @(D)Evil
    Den Satz verstehe ich gar nicht, sorry 🙄
    Was meinst du damit...?

    Vielen Dank für die Hilfe
    jawo3



  • char 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z';

    die Zeile versteh ich nicht...

    char *wort = new char[maxl]; //maxl == maximale laenge
    cin >> wort; 
    cout << endl << "Das Wort lautet verschluesselt: \"" << endl;
    for (unsigned int i = 0; i < strlen(wort); i++)
    {
    cout << (unsigned __int16) wort[i] << ", ";
    }
    

    //for (unsigned int i = 0; wort[i] != '\0'; i++) würde auch gehen - weiß nicht, was eleganter ist, wahrscheinlich diese lösung, weil nicht jedes ma wieder die länge von wort ausgelesen werden muss...

    so in etwa - das wird zwar jz nicht in besonders elegantem c++ gelöst sein aber sollte dir doch verdeutlichen, wie es gemeint war, von wegen ohne switch-block



  • Nachfolgend eine (aus meiner Sicht) eleganten Lösung. Diese habe ich ausführlich Dokumentiert und sollte daher von dir recht einfach nachzuvollziehen sein. Der Lösungsansatz beruht darauf, dass die Zeichen in der ASCII-Tabelle(1) geordnet angelegt sind. Eine ähnliche Methode kann man übrigens Anwenden, wenn man Zeichenketten wie "12345" in Zahlen umwandeln will.

    Und tu uns und vor allem dir den Gefallen: Vermeide goto!

    std::strings sind einfacher (gerade am Anfang) zu händeln, als wilde char-Zeiger (siehe Beispiel).

    Und nochmal: Zeichen sind nur eine andere (geordnete) Darstellungsform von Zahlen. Ein Blick in die Tabelle sollte das entscheidene Klicken in /dev/brain bringen.

    #include <iostream>
    #include <iomanip>  // für setw
    #include <string>
    using namespace std;
    
    void verschluessele(ostream&, const string&);
        // Gibt die Zeichenkette "verschlüsselt" in den entsprechenden
        //  Ausgabestrom
    
    int main(int argc, char** args)
        {
            string nachricht =
                "Diese Nachricht wird nach deinen wuenschen "
                "verschluesselt ausgegeben. Hierbei wird eine einfache "
                "Methode verwendet, die auf der ASCII-Zeichentabelle "
                "basiert."
                ;
    
            verschluessele(cout, nachricht);
    
            return EXIT_SUCCESS;
        }
    
    void verschluessele(ostream& out, const string& str)
        {
            // Nachfolgend wird die Zeichenkette (string) iteriert
            //  (Elementweise durchlaufen). iter ist dann jeweils
            //  ein Zeiger auf das entsprechende Zeichen:
    
            string::const_iterator iter = str.begin();
            for( ; iter != str.end(); iter++) {
    
                char c = *iter;
    
                // Ab hier bestehen drei zu behandelnde Möglichkeiten:
                //  1. Es handelt sich um ein Zeichen zwischen a und z
                //  2. Es handelt sich um ein Zeichen zwischen A und Z
                //  3. Es handelt sich nicht um ein Zeichen der Kategorie
                //      1 und 2.
    
                // In der ASCII-Tabelle liegt a auf 0x61 (dezimal 97)
                //  und z bei 0x7a (dezimal 122).
                //  Analog dazu: A = 0x41, Z=0x5a.
    
                // Der Einfachheit halber werden alle Buchstaben zunächst
                //  in Großbuchstaben umgewandelt.            
                if(c >= 0x61 && c <= 0x74) c -= 0x20;
    
                // Nun ist das Zeichen entweder ein Großbuchstabe oder
                //  ein Zeichen welches nicht umgewandelt (kodiert) wird.
    
                // Handelt es sich nicht um einen Großbuchstaben, wird
                //  das Zeichen ignoriert und die Schleife weiter
                //  durchlaufen:
                if( !(c >= 0x41 && c <= 0x5a) ) continue;
    
                // Andernfalls wird das Zeichen "kodiert". Dies geschieht
                //  aufgrund der Tatsache, dass die Zeichen von A - Z
                //  alphabetisch geordnet in der ASCII-Tabelle ab 0x41
                //  (dezimal 65) hintereinander liegen.
                out << setw(4) << static_cast<int>(c - 64);
            }
            out << endl;
        }
    

    Gruß... Heiko

    ---
    Fußnoten:
    (1): http://www.physik.fu-berlin.de/~goerz/misc/ascii.gif



  • Kleine Ergänzung: Der Übersichtlichkeit halber solltest du lieber die Zeichenklassifikations-Funktionen von C++ verwenden anstelle der kryptischen Hex-Arithmetik:

    for( ; iter != str.end(); iter++) {
      char c = *iter;
    
      // Ab hier bestehen drei zu behandelnde Möglichkeiten:
      //  1. Es handelt sich um ein Zeichen zwischen a und z
      //  2. Es handelt sich um ein Zeichen zwischen A und Z
      //  3. Es handelt sich nicht um ein Zeichen der Kategorie 1 und 2.
    
      // toupper() liefert zu einem Kleinbuchstaben den dazugehörigen Großbuchstaben
      //  und gibt alle anderen Zeichen unverändert zurück
    
      // Der Einfachheit halber werden alle Buchstaben zunächst
      //  in Großbuchstaben umgewandelt.
      c = toupper(c)
    
      // Nun ist das Zeichen entweder ein Großbuchstabe oder
      //  ein Zeichen welches nicht umgewandelt (kodiert) wird.
    
      // Handelt es sich nicht um einen Großbuchstaben, wird
      //  das Zeichen ignoriert und die Schleife weiter
      //  durchlaufen:
      if(!isupper(c))continue;
    
      // Andernfalls wird das Zeichen "kodiert". Dies geschieht
      //  aufgrund der Tatsache, dass die Zeichen von A - Z
      //  alphabetisch geordnet in der ASCII-Tabelle ab 0x41
      //  (dezimal 65) hintereinander liegen.
      out << setw(4) << static_cast<int>(c - 'A' + 1);
    }
    

    (ich hab mal deine Kommentare dringelassen und ergänzt)



  • Das ist natürlich noch besser.

    "Kryptische Hex-Arithmetik" (gibt es Hex-Arithmetik überhaupt?) sind noch Altlasten, die ich nach meiner kurzen "Assembler-Exkursion" mit mir herumtrage (kam direkt nach BASIC). now replaced

    Noch ein letzter Versuch die kryptische Hex-Arithmetik zu rechtfertigen: Ich wollte kurz auf das Arbeiten und Rechnen im ASCII-Zeichensatz verweisen ;).

    Grüße... Heiko



  • bwbg schrieb:

    "Kryptische Hex-Arithmetik" (gibt es Hex-Arithmetik überhaupt?) sind noch Altlasten, die ich nach meiner kurzen "Assembler-Exkursion" mit mir herumtrage (kam direkt nach BASIC). now replaced

    Mit "Hex-Arithmetik" meinte ich die Sammlung an Hex-Zahlen, mit denen du dein Text-Zeichen verrechnest. Und 100% portabel bist du damit auch nicht (auch wenn jeder moderne Compiler damit arbeitet, schreibt der C++ Standard keineswegs vor, daß char im ASCII-Zeichensatz interpretiert wird*)

    * Und es gibt auch Zeichensätze, die inkompatibel zu ASCII sind, z.B. EBCDIC (wobei bei dieser Codierung nichtmal die "klassische" char-Arithmetik funktionieren würde)



  • Damit ist nun auch der letzte Versuch der Rechtfertigung zu nichte gemacht...

    Benutzt die Zeichenklassifikations-Funktionen!

    OT: Aber die Erlösung ist nahe. Um die vielen bösartigen Zeichenkodierungen zu vertilgen wurde in dunklen Labors das Unicode-Monster gezüchtet, welches mit diversen Fangarmen (UTF-8 bis UTF-x, usw.) nach ahnungslosen String-Implementationen jagt...



  • bwbg schrieb:

    OT: Aber die Erlösung ist nahe. Um die vielen bösartigen Zeichenkodierungen zu vertilgen ... UTF-8 bis UTF-x...

    Womit wir weiterhin als Programmierer mit verschiedene Zeichenkodierungen zu kämpfen haben. Es gibt leider nicht EIN Unicode-System sondern mehrere.

    cu André



  • Also ich habe jetzt mit Hilfe eurer Beiträge und Codes ein Programm zusammengebastelt, das so läuft wie ich es haben möchte.

    Hier mal der Source-Code:

    #include <iostream>
    #include <string>
    using namespace std;
    
    void verschluessele(ostream&, const string&);
    
    int main()
        {
            string nachricht;
            cin >> nachricht;
            verschluessele(cout, nachricht);
            return 0;
        }
    
    void verschluessele(ostream& out, const string& str)
        {
        string::const_iterator iter = str.begin();
        for( ; iter != str.end(); iter++)
        {
            char c = *iter;
    
            if (64 < c && c < 90)
            cout << static_cast<int>(c-64) << ", ";
    
            else if (96 < c && c < 123)
            cout << static_cast<int>(c-70) << ", ";
    
            else
            cout << "?";
        }
        cout << endl;
        }
    

    Ich würde Interesse halber gerne wissen, ob man das noch weiter optimieren könnte, denn ich möchte mir natürlich einen möglichst guten Programmierstil angewöhnen.

    Aber bis hierhin schon mal vielen Dank für eure kompetente Hilfe 👍



  • Generell sieht dein Code jetzt schon viel mehr nach C++ aus, als es dein erster Versuch war.

    Du solltest jetzt nur noch die Anmerkung von CStoll bzgl. der Zeichenkonstanten umsetzen ('A' statt 65, etc.)
    und möglichst die Einrückung (bei den if-Anweisungen) verbessern (um es leserlicher zu gestalten):

    if ('A' <= c && c <= 'Z') // besser wäre sogar: isupper(c)
       cout << static_cast<int>(c-'A'+1) << ", ";
    

    Ich gehe jetzt mal vom ASCII-Zeichensatz aus -)



  • Danke für die Antwort.

    Ich musste gerade erstmal ein bisschen suchen, wie man isupper/islower syntaktisch richtig verwendet, da ich die Funktionen noch nie benutzt habe, aber jetzt hat´s funktioniert. Hier mal der Code:

    #include <iostream>
    #include <string>
    #include <cctype>
    using namespace std;
    
    void verschluessele(ostream&, const string&);
    
    int main()
        {
            string nachricht;
            cin >> nachricht;
            verschluessele(cout, nachricht);
            return 0;
        }
    
    void verschluessele(ostream& out, const string& str)
        {
        string::const_iterator iter = str.begin();
        for( ; iter != str.end(); iter++)
        {
            char c = *iter;
    
            if (isupper(c))
                cout << static_cast<int>(c-64) << ", ";
    
            else if (islower(c))
                cout << static_cast<int>(c-70) << ", ";
    
            else
                cout << "?, ";
        }
        cout << endl;
        }
    

    Falls noch jemand eine Optimierungsidee hat, bitte melden.

    Vielen Dank
    jawo3



  • #include <iostream>
    #include <string>
    #include <cctype>
    
    void verschluessele(std::ostream&, const std::string&);
    
    int main() {
    	std::string nachricht;
    	std::cin >> nachricht;
    	verschluessele(std::cout, nachricht);
    	return 0;
    }
    
    void verschluessele(std::ostream& out, const std::string& str) {
    	for(std::string::const_iterator iter = str.begin(); iter != str.end(); ++iter) {
    		const char c = *iter;
    
    		if (isupper(c)) {
    			out << static_cast<int>(c - 'A' + 1) << ", ";
    		}
    		else if (islower(c)) {
    			out << static_cast<int>(c - 'D') << ", ";
    		}
    		else {
                out << "?, ";
    		}
    	}
    	out << "\n";
    }
    

    Habs aber an meinen Programmierstyle angepasst 😛

    Viele Grüße,
    Patrick

    Edit: Ja, Ja... Mir ist eben noch was eingefallen 😃



  • Ich habe mir mal deinen Code angeschaut und da sind mir folgende Frage in den Sinn gekommen:

    Hat es irgendwelche Vorteile/Nachteile, wenn man/ wenn man nicht den "namespace std" benutzt, sondern immer ein std:: vor die einzelnen Funktionen schreibt?

    Was ist der Unterschied zwischen "cout" und "out"?



  • jawo3 schrieb:

    Hat es irgendwelche Vorteile/Nachteile, wenn man/ wenn man nicht den "namespace std" benutzt, sondern immer ein std:: vor die einzelnen Funktionen schreibt?

    Wie gesagt: Ich habe den Code meinem eigenen Programmierstyle angepasst. Und meiner Meinung nach gibt es namespaces nicht umsonst. Ich habe mir also angwöhnt immer std:: zu schreiben 🙂

    jawo3 schrieb:

    Was ist der Unterschied zwischen "cout" und "out"?

    Du hast immer geschrieben:

    verschluessele([b]std::cout[/b], nachricht);
    

    Und ich bin davon ausgegangen, dass du in der Funktion out mit cout verwechselt hast und habe das deswegen angepasst, so das der Parameter auch genutzt wird wie es vermutlich gewollt war 🙂

    Viele Grüße,
    Patrick



  • Bzgl. der Verwendung des 'namespace std' hat jeder Programmierer seine eigene Schreibweise. In Header-Dateien solltest du aber NIEMALS "using namespace std;" schreiben (da du damit die Namensbereiche aushebelst und wieder alle Namen global gemacht werden).
    In Source-Dateien kannst du auch alternativ einzelne Namen am Anfang benennen, um sie dann ohne 'std::' schreiben zu können:

    using std::string;
    using std::cout;
    // ...
    

    Eine weitere Alternative besteht dadrin, "using namespace std;" nur innerhalb von Funktionen zu verwenden:

    void printMyName()
    {
      using namespace std;
    
      string s("Hallo");
      cout << s << "Th!";
    }
    

    Und du hast bei dir zwar 'out' als Parameter übergeben, aber nicht verwendet (hatte ich vorhin gar nicht bemerkt), sondern immer auf die Konsole ausgegeben.
    Du könntest z.B. alternativ die Daten mithilfe eine ofstream-Objekts z.B. auch in eine Datei schreiben:

    ofstream of("Test.txt");
    verschluessele(of, nachricht);
    


  • Okay, vielen Dank.



  • und ganz generell: bei switch-case auch immer

    break;
    

    nach jedem block einfügen. sonst könnte die verschlüsselung sicherlich spassiger werden, als angedacht



  • Wird Switch nicht automatisch beendet wenn ein case eingetreten ist?



  • Nein.



  • Kommt drauf an, was du mit "beendet" meinst - der switch sucht die erste case-Marke, die zum Wert passt, und läuft von dort bis zum Blockende:

    int i=1;
    switch(i)
    {
    case 0: cout<<0;
    case 1: cout<<1;
    case 2: cout<<2;
    default:cout<<"unbekannt";
    }
    

    Die Ausgabe davon ist "12unbekannt"


Anmelden zum Antworten