Javakurs/Übungsaufgaben/Cäsar-Chiffre/Musterloesung
< Javakurs | Übungsaufgaben | Cäsar-Chiffre
Cäsar-Chiffre - Musterlösung
Fas Verschlüsseln und Entschlüsseln wurde der Übersichtlichkeit halber in zwei Klassen aufgeteilt, welche eigenständig lauffähig sind.
CaesarEncode.java (Cäsar-Verschlüsselung)
Diese Klasse liest eine Datei ein, verschlüsselt sie und speichert sie anschließend.
package caesar; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.LinkedList; /** * Programm welches eine Textdatei mit der Caesar-Verschiebung verschlüsselt.<br> * Der Schluessel ist eine genze Zahl zwischen 0 und 26, * um die alle "normalen" Buchstaben verschoben werden.<br> * (nur <code>a..z</code> und <code>A..Z</code>)<br> * Der Originaltext wird aus einer Datei eingelesen und * verschlüsselt in eine andere geschrieben.<br> * (eine vorhandene Datei wird dabei ueberschrieben)<br> * Parameterliste: Dateiname (Eingabe), Schluessel, Dateiname (Ausgabe) * @author André Schulz * @version 1.0 (3/2009) */ public class CaesarEncode { /** * Main-Methode fuehrt das Programm aus.<br> * Programm verschiebt alle Buchstaben um <code>Schluessel</code> (zwischen 0 und 26). * @param args [Dateiname fuer Eingabe, Schluessel, Dateiname fuer Ausgabe] * @throws IOException falls Fehler beim Lesen der Datei auftreten */ public static void main(String[] args) throws IOException { // bei falscher Argument-Länge wird die Parameterliste ausgegeben if (args.length != 3) { System.out.println("params: <fileIn> <key> <fileOut>"); // beenden des Programms System.exit(0); } // ==++== lesen der Eingabedatei (Klartext) System.out.println("reading");// Fortschrittausgabe // erstellt Reader um aus Datei zu lesen BufferedReader br = new BufferedReader(new FileReader(args[0])); // liest eine Zeile aus der Datei ein String line = br.readLine(); // Liste für die verschlüsselten Zeilen LinkedList<String> encodedLines = new LinkedList<String>(); int key = -1;// initialisieren der Variable für die Verschiebung mit default-Wert // Ermitteln des Schluessels und Fehlerbehandlung try { // wandelt den String aus dem 2. Parameter in eine Zahl um key = Integer.parseInt(args[1]); } catch (NumberFormatException e) { // wenn der String nicht in eine ganze Zahl umgewandelt werden konnte // wird eine Fehlermeldung ausgegeben und beendet System.out.println("could not parse key ('" + args[1] + "' is no number!)"); System.exit(0); } // Ueberpruefung ob der Schluessel im gueltigen Bereich liegt if (key < 0 || key > 26) { // Fehlermeldung wenn Schluessel ausserhlab Bereich System.out.println("key must be 0..26"); } // es werden so lange Zeilen gelesen, wie vorhanden while (line != null) { // fuegt die verschluesselten Zeilen der Zeilen-Liste hinzu encodedLines.add(encode(line, key)); line = br.readLine();// lesen der naechsten Zeile } // schliesst den Reader, da nicht mehr benoetigt // (Datei ist wieder fuer andere Programme frei) br.close(); // ==--== lesen der Eingabedatei (Klartext) // ==++== schreiben der Ausgabedatei (verschluesselt) System.out.println("writing output...");// Fortschrittausgabe // erstellt Writer um in eine Datei zu schreiben (vorhandene wird ueberschrieben) BufferedWriter bw = new BufferedWriter(new FileWriter(args[2])); // Schleife lauft ueber alle Elemente in der Zeilen-Liste for (String lineOut : encodedLines) { bw.write(lineOut);// schreibt aktuelle Zeile in Datei bw.write("\n");// schreibt Zeilenumbruch in Datei // bw.write("\r\n");//schreibt Zeilenumbruch in Datei (Windows-Version) } // schliesst Writer, da nicht mehr benoetigt bw.close(); // ==--== schreiben der Ausgabedatei (verschluesselt) System.out.println("finsh");// Fortschrittausgabe }// main /** * Methode, welche einen String nach Caesar verschiebt. * @param text Text der zu verschluesseln ist * @param key Schluessel, um den Buchstaben verschoben werden sollen (0..26) * @return verschluesselter Text */ private static String encode(String text, int key) { // erstellt neuen StringBuilder, um Ausgabe-String zu generieren StringBuilder sb = new StringBuilder(); int newC;// Code eines alten buchstaben int oldC;// Code des Buchstaben in neu // Schleife durchlauft alle Zeichen while (!text.isEmpty()) { // der erste Buchstabe wird gelesen newC = oldC = text.charAt(0); // Ueberpruefung ob Zeichen ein Buchstabe ist, // dann wird umgewandelt (ansonsten bleibt Zeichen erhalten) if (oldC >= 'a' && oldC <= 'z') { // Kleinbuchstabe newC = oldC + key;// Verschluesselung if (newC > 'z')//Behandlung von Overflows { newC -= 26;// anpassen des Buchstaben }// if } else if (oldC >= 'A' && oldC <= 'Z') { // Grossbuchstabe newC = oldC + key;// Verschluesselung if (newC > 'Z')//Behandlung von Overflows { newC -= 26;// anpassen des Buchstaben }// if }// if else { // nix, da nur "normale" Buchstaben verschluesselt werden } // das aktuelle Zeichen (der verschluesselte Buchstabe) //wird dem String hinzugefuegt sb.append((char) newC); // der momentane Text wird auf den Rest (one erstes Zeichen) gesetzt text = text.substring(1); }// while return sb.toString();// verschluesselter String wird zurueckgegeben }// encode }//classs
CaesarEncode.java (Cäsar-Entschlüsselung)
Diese Klasse entschlüsselt eine verschlüsselte Datei. Dazu gibt es zwei Möglichkeiten:
- man gibt den Schlüssel an nach dem entschlüsselt werden soll (wenn bekannt)
- man gibt keinen Schlüssel an und das Programm ermittelt den Schlüssel.
Bei beiden Versionen wird die entschlüsselte Datei anschließend gespeichert.
package caesar; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.LinkedList; /** * Programm welches eine mit der Caesar-Verschiebung verschlüsselte Textdatei entschluesselt.<br> * Der Schluessel wird entweder als Parameter uebergeben oder durch das Programm ermittelt.<br> * Bei der eigenstaendigen Ermittlung wird nach dem haefigsten Buchstaben gesucht, * welcher dann das <code>e</code> sein muesste.<br> * Parameterliste:<br> * entweder: Dateiname (Eingabe), Schluessel, Dateiname (Ausgabe)<br> * oder: Dateiname (Eingabe), Dateiname (Ausgabe) * @author André Schulz * @version 1.0 (3/2009) */ public class CaesarDecode { public static void main(String[] args) throws IOException { // Ueberpruefung ob Parameteranzahl falsch ist if (args.length < 2 || args.length > 3) { // Ausgabe der parameterliste und beenden des Programms System.out.println("params: <fileIn> [<key>] <fileOut>"); System.exit(0); } int key = -1;// Schluessel (Initialisierung mit default-Wert) String fileNameout;// Name der Ausgabedatei // erstellt Reader um aus verschluesselter Datei zu lesen BufferedReader br = new BufferedReader(new FileReader(args[0])); System.out.println("reading");// Fortschrittausgabe String line = br.readLine();// liest erste Zeile // Liste fuer die gelesenen Zeilen LinkedList<String> lines = new LinkedList<String>(); // Schleife so lange, wie gelesene Zeilen Text enthalten while (line != null) { lines.add(line);// hinzufuegen der Zeile zur Liste line = br.readLine();// lesen der naechsten Zeile } // Schliessen der Datei br.close(); // Auswahl je nach Parameteranzahl if (args.length == 3) { // 3 Parameter: Schluessel vom Benutzer gegeben try { // wandelt den String aus dem 2. Parameter in eine Zahl um key = Integer.parseInt(args[1]); } catch (NumberFormatException e) { // wenn der String nicht in eine ganze Zahl umgewandelt werden konnte // wird eine Fehlermeldung ausgegeben und beendet System.out.println("could not parse key ('" + args[1] + "' is no number!)"); System.exit(0); } // Ueberprueft Schluessel auf Gueltigkeit if (key < 0 || key > 26) { // Fehlerausgabe wenn ungueltig und beendung des Programms System.out.println("key must be 0..26"); System.exit(0); } // setzt Dateiname fuer Ausgabe mit Wert aus 3. Parameter fileNameout = args[2]; } else { // nicht 3 Parameter => 2 Parameter // Schluessel ist niciht gegeben uns muss ermittelt werden // Schluesselermittlung an Hand der gelesenen Zeilen key = determineKey(lines); fileNameout = args[1];// Dateiname fuer Ausgabe System.out.println("key:" + key);// Ausgabe des gefundenen Schluessels }// else System.out.println("writing output...");// Fortschrittausgabe // Writer zum Schreiben der entschluesselten Datei BufferedWriter bw = new BufferedWriter(new FileWriter(fileNameout)); // Schleife geht alle gelesenen Zeilen durch for (String lineOut : lines) { // jede Linie wird separat decodiert und dann in Datei geschrieben bw.write(decode(lineOut, key)); bw.write("\n");// Zeilenumbruch // bw.write("\r\n");//schreibt Zeilenumbruch in Datei (Windows-Version) } // Schliessen der Datei bw.close(); System.out.println("finsh");// Fortschrittausgabe }// main /** * Methode ermittelt den Schluessel an Hand des Textes. * @param lines Text als Liste der Zeilen * @return Schluessel mit dem verschluesselt wurde */ private static int determineKey(LinkedList<String> lines) { int i;// Zaehlvariable (wird mehr mahls verwendet) int[] lower = new int[26];// Array fuer Haufigkeit der Kleinbuchstaben int[] upper = new int[26];// Array fuer Haufigkeit der Grossbuchstaben char c;// aktuelles Zeichen // ==++== Ermittlung der einzelnen Haeufigkeiten // Schleife durchlauft alle eingelesenen Zeilen for (String line : lines) { // Zeile wird Zeichen fuer Zeichen durchlaufen for (i = 0; i < line.length(); i++) { c = line.charAt(i);// Zeichen wird gelesen // wenn Zeichen ein buchstabe ist, wird Stelle im Array incrementiert if (c >= 'a' && c <= 'z') { // Kleinbuchstabe lower[c - 'a']++; } else if (c >= 'A' && c <= 'Z') { // Grossbuchstabe upper[c - 'A']++; } }// for }// for // ==--== Ermittlung der einzelnen Haeufigkeiten // Ausgabe der einzelnen Haeufigkeiten c = 'a';// angefangen bei "a"... for (i = 0; i < 26; i++) { // Ausgabe des Buchstaben und der Haeufigkeit bei Klein- und Grossbuchstaben System.out.println(c + "\t" + lower[i] + "\t" + upper[i]); c++;// weiter zum naechsten Buchstaben } //==++== Suche nach dem haeufigsten Buchstaben c = ' ';// default "Buchstabe" int max = -1;// groesste gefundene Haeufigkeit // Durchlaufen beider Arrays parallel for (i = 0; i < 26; i++) { // wenn aktueller Buchstabe haeufiger als anderer zuvor if (lower[i] + upper[i] > max) { // merken der groessten Haeufigkeit max = lower[i] + upper[i]; // merken des Buchstaben c = (char) ('a' + i);// Rechnung mit Zeichen (z.B.: 'a'+2='c') } }// for //==--== Suche nach dem haeufigsten Buchstaben // theoretisch steht jetzt in der Variable c der Buchstabe mit der // groessten Haeufigkeit im eingelesen Text, da bei deutschen Texten das 'e' // am Haeufigsten auftritt muss dies ein verschluesseltes 'e' sein int key = c - 'e';// ausrechnen des Schluessels // anpassen des Schluessels bei einem Overflow if (key < 0) { key += 26; } return key;// ermittelter Schluessel wird zurueckgegeben }// countLetter /** * Methode decodiert einen Text bei gegebenen Schluessel. * @param text zu entschluesselnder Text * @param key Schluessel, mit dem verschluesslt wurde * @return entschluesselter Text */ private static String decode(String text, int key) { // neuer StringBuilder um Ausgabetext zu "bauen" StringBuilder sb = new StringBuilder(); int newC;// Code eines alten buchstaben int oldC;// Code des Buchstaben in neu // Schleife so lange der Text noch Zeichen enthaelt while (!text.isEmpty()) { // einlesen des ersten Zeichens newC = oldC = text.charAt(0); // Ueberpruefung ob Zeichen ein normaler Buchstabe ist if (oldC >= 'a' && oldC <= 'z') { // Kleinbuchstabe newC = oldC - key;// Entschluesselung if (newC < 'a')// Behandlung von Overflows { newC += 26;// anpassen des Buchstaben }// if } else if (oldC >= 'A' && oldC <= 'Z') { // Grossbuchstabe newC = oldC - key;// Entschluesselung if (newC < 'A')// Behandlung von Overflows { newC += 26;// anpassen des Buchstaben }// if }// if else { // nix, da nur "normale" Buchstaben verschluesselt werden } // entschluesselter Buchstabe wird Text hinzugefuegt sb.append((char) newC); // der momentane Text wird auf den Rest (one erstes Zeichen) gesetzt text = text.substring(1); }// while return sb.toString();// entschluesselter text wird zurueckgegeben }// encode }//class