Caesar verschlüsselung, problem?
Ich soll für den Informatikunterricht ein Verschlüsselungsprogramm erstellen welches die Caesarverschlüsselung benutzt, jedoch kriege ich für jeden Buchstaben die gleiche ausgabe unzwar so kleine fragezeichen, bitte um hilfe
Mein Programm:
import java.util.Scanner;
public class Caesar {
public static void main (String[] args) {
Scanner scanner = new Scanner (System.in);
System.out.println("Zu verschlüsselnden Text eingeben:");
String text = scanner.nextLine();
// Text eingeben
int offset = 3;
// Offset (Verschiebung) wählen
char [] meinArray = text.toCharArray();
// den Text in ein Char Array laden
char [] meinNeuesArray = verschluesseln(offset, meinArray);
// mit der Methode (siehe unten) "verschluesseln" ein neues
// Char Array mit dem verschlüsselten Text erstellen
System.out.println("So sieht der Text verschlüsselt aus:");
// verschlüsselten Text wieder ausgeben:
for (int i = 0; i < meinNeuesArray.length; i++) {
System.out.print(meinNeuesArray[i]);
}
scanner.close();
}
// hier die Methode zum verschlüsseln
public static char[] verschluesseln(int offset, char[] charArray) {
char[] cryptArray = new char[charArray.length];
// ein leeres Char Array erstellen
for (int i = 0; i < charArray.length; i++) {
int verschiebung = (charArray[i] + offset)%26;
// ursprüngliches Zeichen plus Offset modulo 26
cryptArray[i] = (char) (verschiebung);
}
return cryptArray;
}
}
3 Antworten
Schau Dir mal in der ASCII-Tabelle an, wie die Schriftzeichen numeriert sind. Das Alphabet beginnt nicht bei 0. Das große A hat die Nummer 65.
Diese 65 mußt Du berücksichtigen, wenn Du die Verschiebung vornimmst, und zwar einmal, bevor Du den Offset hinzuaddierst und die Modulo-Operation ausführst, und hinterher noch einmal:
int verschiebung = (charArray[i] - 65 + offset)%26 + 65;
So ist es. Mit kleinen Buchstaben kommt dieser Algorithmus nicht klar, mit Umlauten schon gar nicht, und was noch wichtiger ist: noch nicht einmal mit dem Leerzeichen.
Es müßte oben eigentlich heißen: "Zu verschlüsselnden Text eingeben (nur Großbuchstaben, keine Leerzeichen):"
Aber das kann man nicht alles auf einmal erklären.
Im Zeichensatz sind die ersten 26Zeichen einfach keine Buchstaben.
Jeder Zeichensatz hat mindestens 256 Zeichen.
ASCII hat nur 127 Zeichen - also nicht jeder Zeichensatz hat 256 Zeichen. Und das Null-Zeichen ist häufig (z.B. in C) kein Zeichen im eigentlichen Sinne, also bleiben 255 Zeichen. Ansonsten stimmt obige Lösung für die 26 Buchstaben des Alphabets, aber ohne Umlaute etc.
Sonst sind es am Computer die ersten 32 Zeichen (0-31), welche Sonderzeichen (z.B. 8=Tabulator, 10=Zeilenvorschub, 13=Zeilenrücklauf) darstellen und die daher nicht einfach ausgegeben werden dürfen. Danach kommen die Ziffern, die will man sicher auch kodieren.
Also vorher sicherstellen, dass kein Zeichen < 32 oder > 255 eingegeben wurde, und dann:
int verschiebung = (charArray[i] - 32 + offset)%255 + 32;
(Nur um dieser Frage nach 2 Jahren endlich eine allgemeine funktionierende Antwort zu verpassen)
Dein Problem liegt hier:
int verschiebung = (charArray[i] + offset)%26;
Jedes Zeichen ist ein UTF-16 Code zwischen 0 (='\u0000') und 65535 (='\uffff'):
- Die Werte 0-31, 127, und 128-159 sind Steuerzeichen (Tabulator, Zeilenvorschub, Bildschirm löschen, ...). Diese solltest Du nicht verschlüsseln.
- Zwischen 32 und 126 liegen die ASCII-Zeichen, die jedes Terminal darstellen kann. Diesen Bereich kann man problemlos verschlüsseln. Dabei können Buchstaben in Sonderzeichen umgewandelt werden. Willst Du ausschließlich Buchstaben codieren, beschränke Dich auf die Teilbereiche 'A'-'Z' (65-90) und 'a'-'z' (97-122).
- Zwischen 160 (='\u00a0') und '\ud7ff' liegen weitere Zeichen wie Umlaute, chinesische Zeichen, Symbole. Was davon angezeigt werden kann, hängt stark vom Terminal und den installierten Fonts ab. Beim Verschlüsseln riskierst Du, dass gängige Zeichen (ä⇒ß±µ²) in unlesbare Zeichen umgewandelt werden. Das erschwert das Debuggen.
- Ab '\ud800' wird's gefährlich, denn je zwei solcher Codes bilden zusammen ein (sehr seltenes) Zeichen, und nicht jede Kombination ist zulässig. Ich würde solche Codes auf keinen Fall umwandeln.
Du brauchst also unbedingt eine Fallunterscheidung. Dazu würde ich eine Funktion für ein einzelnes Zeichen schreiben:
char verschlüssle(char c, int offset) {
if (c < ' ')// Steuerzeichen
return c;
if (c < 'A') // ASCII: Sonderzeichen, Ziffern
return c;
if (c <= 'Z') { // ASCII: A-Z
c += offset;
if (c > 'Z') c -= 'Z'-'A'+1; // oder: -=26
return c;
}
if (c < 'a') // ASCII: Sonderzeichen
return c;
if (c <= 'z') { // ASCII: a-z
c += offset;
if (c > 'z') c -= 'z'-'a'+1; // oder: -=26
return c;
}return c; // 127 und non-ASCII
}
In Deiner Schleife schreibst Du dann nur noch:
cryptArray[i] = verschlüssle(charArray[i], offset);
Damit bildest Du z.B. 'G' (71) und 'a' (97) auf den gleichen Wert ab.