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