Frage zu einfachem Verschlüsselungslgorithmus
-
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,
PatrickEdit: 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"
-
Achso okay.
Jetzt habe ich aber ein neues Problem:
Ich würde gerne versuchen, ein Programm zuschreibn, was meine Verschlüsselung rückgängig macht.
Es wird also eine Eingabe dieser Form erwartet z.B. 23, 12, 1, 1, 3, 5
und daraus soll das entsprechende Wort ausgegeben werden.
Mit meinem derzeitigen Programm ist das nicht möglich, da:
1. ich nicht weiß wie man zwei Werte gleichzeitig überprüfen kann (also z.B 22 würde normalerweise als 2 und 2 behandelt werden)
2. ich nicht weiß wie man den automatischen Abbruch umgeht, wenn ein eingegebenes Leerzeichen erreicht wird.
Vielleicht könntet ihr mir einige Tipps oder Informationen zu nötigen Funktionen geben, mit denen ich ein solches Programm erstellen kann...
Vielen Dank
jawo3