I need to make a program that will encrypt and decrypt what the user enters. I am having trouble figuring out a way that will combine all the chars to make the encrypted word. Here is my code (I am using Eclipse):
import java.util.Scanner;
public class Encryption
{
public static String message = "";
public static boolean hasMessage = false;
public static boolean encrypted = false;
static char a = 0;
static char b;
static int w;
static int x;
static int y;
static int z;
static int i;
public static void display()
{
System.out.println("Message: " + message + "\n");
}
public static void encrypt(String word)
{
if(!hasMessage)
{
System.out.println("No message");
// Tell the user there is no message
}
else if(encrypted)
{
System.out.println("Message is already encrypted");
// Tell the user the message is already encrypted
}
else
{
// Reset the message to blank
for (int i = 0; i < message.length(); i++) {
i = j;
``char a = message.charAt(i);
for (int j=0; j==message.length(); j++)
{
int w = (int) a * 2;
int x = (int) w + 2;
char y = (char) x;
}
}
}
=
//get char from each letter (increase char each time), cast as int
}
System.out.println(message);
encrypted = true;
// Using the parameter word, modify message
// to contain a new value following a predictable pattern
// Hint: alter each character's ASCII value by casting
// to an integer and using math operators
// Display the new message
// Set encrypted to true
}
public static void decrypt(String word)
{
if(!hasMessage)
{
System.out.println("No message");
// Tell the user there is no message
}
else if(!encrypted)
{
System.out.println("Message not encrypted");
// Tell the user the message is not encrypted
}
else
{
int a = (int) w / 2;
int w = (int) x - 2;
char x = (char) y;
System.out.println(message);
// Like encrypt, but in reverse
}
}
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
int menuChoice = 0;
while(menuChoice != 4)
{
System.out.println( "[1] Enter Word\n" +
"[2] Encrypt\n" +
"[3] Decrypt\n" +
"[4] Quit\n");
menuChoice = sc.nextInt();
if(menuChoice == 1)
{
System.out.println("Input message");
message = sc.next();
// prompt user to input message
// assign next word to the class-level variable message
hasMessage = true;
encrypted = false;
// set hasMessage to true
// set encrypted to false
}
else if(menuChoice == 2)
{
encrypt(message);
}
else if(menuChoice == 3)
{
decrypt(message);
}
}
}
}
The trouble probably from the for loop. I don't really know what this for loop should do so I can't help you to change it.
for (message.charAt(a); a==message.length(); a++)
{
int w = (int) a * 2;
int x = (int) w + 2;
char y = (char) x;
}
What this does in pseudocode:
Ask for the character at 0 in string a and do nothing. This is only wasting time so I don't know what this actually is for.
Check if the message is empty if yes go on otherwise quite the loop.
Execute the code in this loop.
I guess what you want to do is loop through the string char by char like this:
for (int i = 0; i < message.length(); i++) {
char a = message.charAt(i);
}
Related
Hey guys I have been learning coding recently and got an assignment of finding the mean, median and mode of a number of integers in an integer array. The issue that I am facing is that my median and mode displays -1 and I'm not too sure how to fix it, my "if user inputs no, print an error statement" is also not working and I would be very grateful if someone could help me out.
This is my code:
package com.company;
import java.io.File;
import java.io.FileNotFoundException;
import static com.company.ProjectConstants.*;
import java.util.*;
public class Main {
public static void main(String[] args) {
int[] a = new int[MAXDATA];
int counter = 0;
boolean fileDone = false;
boolean inputOk;
String userInput;
String theDataFile;
Scanner s = new Scanner(System.in);
genProgInfo();
userInput = s.nextLine();
userInput = userInput.toLowerCase();
while (!userInput.equals("yes") && (!userInput.equals("no"))) {
System.out.println("ERROR: Please input either yes or no: ");
userInput = s.nextLine();
userInput = userInput.toLowerCase();
}
inputOk = userInput.equals("yes");
initDataStorage(a);
//do {
try {
// create file & scanner objects
System.out.println("enter one of the file names:\nData10File.txt\nData30file.txt\nData35file.txt");
theDataFile = s.next();
theDataFile = theDataFile.toLowerCase();
//fName = userInput;
File f = new File(theDataFile);
Scanner sc = new Scanner(f);
// store file data in array, a
for (int i = 0; i < MAXDATA; i++) {
if (sc.hasNext()) {
a[i] = sc.nextInt();
} else {
fileDone = true;
sc.close();
break;
}
}
// print error message if file data exceeds the range of array
if (!fileDone) {
System.out.println("\n\tCAUTION: file has additional data, consider making array larger.");
}
} catch (FileNotFoundException e) {
System.out.println(e);
e.printStackTrace();
}
//} while (inputOk);
s.close();
for (int i=0; i<MAXDATA; i++) {
if (a[i] != -1) {
counter = a[i];
}
}
System.out.println("counter: "+ counter);
displayResults(calcMean(a), calcMedian(a), calcMode(a));
}
public static void initDataStorage(int[] data) {
for (int i = 0; i < MAXDATA; i++) {
data[i] = INVALID;
}
}
public static double calcMean(int[] data) {
int counter = 0;
int mean;
int sum = 0;
for (int i = 0; i < MAXDATA; i++) {
if (data[i] != -1) {
sum += data[i];
counter++;
}
}
mean = sum / counter;
return mean;
}
public static double calcMedian(int[] data) {
int middle = data.length / 2;
if (data.length % 2 == 1) {
return data[middle];
} else {
return (data[middle -1] + data[middle]) / 2.0;
}
}
public static int calcMode(int[] data) {
int mode = 0, maxCount = 0;
for (int i = 0; i < data.length; ++i) {
int count = 0;
for (int j = 0; j < data.length; ++j) {
if (data[j] == data[i]) {
count++;
}
}
if (count > maxCount) {
maxCount = count;
mode = data[i];
}
}
return mode;
}
public static void genProgInfo() {
System.out.println("This program will calculate the mean, median, and mode of a number of integers stored in the array");
System.out.println("Would you like to continue?");
}
public static void displayResults(double mean, double median, int mode) {
System.out.println("mean: " + mean);
System.out.println("median: " + median);
System.out.println("mode: " + mode);
}
}
I'm assuming the value of INVALID is -1 based on the other code I see and your description of what it outputs. Your array is of length MAXDATA and is initially filled with value INVALID in all elements. You then fill it with n values where n may be less than MAXDATA, and in that (probably common) case, many or even most of the values in the array are the INVALID value.
Your calcMean function is correctly skipping over the -1 (INVALID?) values and not including them in the calculation. Note however that the valid values are all at the beginning of the array and once you find an invalid value, you could break out of the loop in calcMean.
But the calcMedian and calcMode functions are not accounting for the invalid values. If n is significantly less than MAXDATA, then -1 probably really is the mode. Your calcMedian function has an additional problem as well, in that the (valid) data needs to be sorted in order for the "middle" or median value to be in the middle of the array.
Bonus question for your assignment: What if -1 occurs in the input file?
I've created a wonderfully working program that encrypts the text a person enters with a keyword of their choice. The program can decrypt the encrypted message/text by going in decrypt mode and entering the correct key.
Now my problem is that when I enter a text in the Linux shell that's longer than approximately 5000 characters, it doesn't process all the text. I used this German dummy text that's 15954 characters long. If I enter the text in the Linux shell It cuts off my text at about 4025 characters. On the other hand, if I execute the program in IntelliJ everything works like a charm. If you need the code I got on git here.
Thanks in advance. I appreciate the help.
My Start class that starts everything
public class Start
{
public static void main(String[] args)
{
Scanner scanner = new Scanner(System.in);
System.out.print("Would you like to encrypt or decrypt a message? [d/e]\n>");
char de = scanner.nextLine().charAt(0);
boolean _Encrypt = true;
if (de == 'd')
_Encrypt = false;
else if (de == 'e')
;
else
{
System.out.println("Non valid input. Please try again later.");
System.exit(1);
}
char[] text = ch.wagner.root.viginere.Input.getText("Text:\n>");
char[] key = ch.wagner.root.viginere.Input.getKey("Key:\n>");
if (_Encrypt)
{
System.out.println(ch.wagner.root.viginere.Process.encrypt(text, key));
}
else
{
System.out.println(ch.wagner.root.viginere.Process.decrypt(text, key));
}
}
}
My Input class
public class Input
{
public static char[] getText (String requestMessage)
{
boolean _AllowedToContinue = false;
char[] text = null;
while (!_AllowedToContinue)
{
System.out.print(requestMessage);
text = ch.wagner.root.viginere.Input.getCharArray();
if (!Input.inputValid(new String(text), "[^`^]+$"))
System.out.println("Invalid input. Try again.");
else
_AllowedToContinue = true;
}
// System.err.println(new String(text).length());
// Created this to check the length of the String after input
return text;
}
public static char[] getKey (String requestMessage)
{
boolean _AllowedToContinue = false;
char[] key = null;
while (!_AllowedToContinue)
{
System.out.print(requestMessage);
key = ch.wagner.root.viginere.Input.getCharArray();
if (!Input.inputValid(new String(key), "^[A-Za-z]+$"))
System.out.println("Invalid input. Try again.");
else
_AllowedToContinue = true;
}
return key;
}
private static boolean inputValid (String input, String regex)
{
boolean returnValue = true;
final Pattern pattern = Pattern.compile(regex);
if (!pattern.matcher(input).matches()) {
returnValue = false;
}
return returnValue;
}
private static String getString ()
{
Scanner scanner = new Scanner(System.in);
return scanner.nextLine();
}
private static char[] getCharArray ()
{
return getString().toCharArray();
}
}
The Process class that encrypts/decrypts the text
public class Process
{
public static String decrypt(char[] text, char[] key)
{
int[] shift = new int[key.length];
key = new String(key).toUpperCase().toCharArray();
for (int i = 0; i < shift.length; i++)
shift[i] = key[i] - 65;
int ii = 0;
for (int i = 0; i < text.length; i++)
{
if (ii == shift.length)
ii = 0;
if (Character.isLetter(text[i]))
{
if (((((text[i] - 65) - shift[ii]) % 26) + 65) < 65)
text[i] = (char) (((((text[i] - 65) - shift[ii]) % 26) + 65) + 26);
else
text[i] = (char) ((((text[i] - 65) - shift[ii]) % 26) + 65);
}
ii++;
}
return new String(text);
}
public static String encrypt(char[] text, char[] key)
{
int[] shift = new int[key.length];
key = new String(key).toUpperCase().toCharArray();
for (int i = 0; i < shift.length; i++)
{
shift[i] = key[i] - 65;
}
text = new String(text).replaceAll("[^a-zA-Z0-9|/()*]+", "").toCharArray();
int ii = 0;
for (int i = 0; i < text.length; i++)
{
if (ii == shift.length)
ii = 0;
text[i] = Character.toUpperCase(text[i]);
if (Character.isLetter(text[i]))
text[i] = (char)((((text[i] - 65) + shift[ii]) % 26) + 65);
ii++;
}
return new String(text);
}
}
I want to scan a String and its content. I want to return an error message if there are any character in the string. For example: int a = myFunction("123"); will save "123" in a, but when the user tries to do something like int a = myFunction("12s32"); it should return the error, because there is a character in the string. This is what i got so far:
public class Parseint {
public static int parseInt(String str) {
if (str == null || str.isEmpty()) {
System.out.println("Der String enthaelt keine Zeichen");
return -1;
} else {
int sum = 0;
int position = 1;
for (int i = str.length() - 1; i >= 0; i--) {
int number = str.charAt(i) - '0';
sum += number * position;
position = position * 10;
}
return sum;
}
}
public static void main(String[] args) {
int a = parseInt("");
System.out.println(a);
}
}
Doing a try and catch like in this link suggests would work perfectly
A simple try and catch
I have 2 java programs (ciphers), one is Playfair and second is Transposition.
Now i want to run Playfair code, then right after that compile Transposition using the result i got from Playfair code. How should i make this?(
Playfair code
import java.awt.Point;
import java.util.Scanner;
public class PlayfairCipher {
private static char[][] charTable;
private static Point[] positions;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String key = prompt("Enter an encryption key (min length 6): ", sc, 6);
String txt = prompt("Enter the message: ", sc, 1);
String jti = prompt("Replace J with I? y/n: ", sc, 1);
boolean changeJtoI = jti.equalsIgnoreCase("y");
createTable(key, changeJtoI);
String enc = encode(prepareText(txt, changeJtoI));
System.out.printf("%nEncoded message: %n%s%n", enc);
System.out.printf("%nDecoded message: %n%s%n", decode(enc));
}
private static String prompt(String promptText, Scanner sc, int minLen) {
String s;
do {
System.out.print(promptText);
s = sc.nextLine().trim();
} while (s.length() < minLen);
return s;
}
private static String prepareText(String s, boolean changeJtoI) {
s = s.toUpperCase().replaceAll("[^A-Z]", "");
return changeJtoI ? s.replace("J", "I") : s.replace("Q", "");
}
private static void createTable(String key, boolean changeJtoI) {
charTable = new char[5][5];
positions = new Point[26];
String s = prepareText(key + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", changeJtoI);
int len = s.length();
for (int i = 0, k = 0; i < len; i++) {
char c = s.charAt(i);
if (positions[c - 'A'] == null) {
charTable[k / 5][k % 5] = c;
positions[c - 'A'] = new Point(k % 5, k / 5);
k++;
}
}
}
private static String encode(String s) {
StringBuilder sb = new StringBuilder(s);
for (int i = 0; i < sb.length(); i += 2) {
if (i == sb.length() - 1)
sb.append(sb.length() % 2 == 1 ? 'X' : "");
else if (sb.charAt(i) == sb.charAt(i + 1))
sb.insert(i + 1, 'X');
}
return codec(sb, 1);
}
private static String decode(String s) {
return codec(new StringBuilder(s), 4);
}
private static String codec(StringBuilder text, int direction) {
int len = text.length();
for (int i = 0; i < len; i += 2) {
char a = text.charAt(i);
char b = text.charAt(i + 1);
int row1 = positions[a - 'A'].y;
int row2 = positions[b - 'A'].y;
int col1 = positions[a - 'A'].x;
int col2 = positions[b - 'A'].x;
if (row1 == row2) {
col1 = (col1 + direction) % 5;
col2 = (col2 + direction) % 5;
} else if (col1 == col2) {
row1 = (row1 + direction) % 5;
row2 = (row2 + direction) % 5;
} else {
int tmp = col1;
col1 = col2;
col2 = tmp;
}
text.setCharAt(i, charTable[row1][col1]);
text.setCharAt(i + 1, charTable[row2][col2]);
}
return text.toString();
}
}
and Transposition
import java.util.*;
import java.util.Scanner; // needed for Scanner
public class transpositionCipher
{
public static void main(String args[])
{
String key;
String message;
String encryptedMessage;
// Letters in the x-axis
int x=0;
// Letters in the y-axis
int y=0;
// Prompt the user
System.out.print( "Type your Key : " );
// Read a line of text from the user.
Scanner scan = new Scanner(System.in);
key = scan.nextLine();
// Display the input back to the user.
System.out.println( "Your Key is " + key );
//Prompt the user
System.out.print( "Type your Message : " );
//Read a line of text from the user.
message = scan.nextLine();
//Display the input back to the user.
System.out.println( "Your Message is " + message );
int msgchar = message.length();
int keycahr = key.length();
if (!((msgchar % keycahr) == 0)){
do{
message = message + "x";
msgchar = message.length();
}while(!((msgchar % keycahr) == 0));
}
encryptedMessage = "";
// To set the temp as [x][y]
char temp[][]=new char [key.length()][message.length()];
char msg[] = message.toCharArray();
// To populate the array
x=0;
y=0;
// To convert the message into an array of char
for (int i=0; i< msg.length;i++)
{
temp[x][y]=msg[i];
if (x==(key.length()-1))
{
x=0;
y=y+1;
} // Close if
else
{
x++;
}
} // Close for loop
// To sort the key
char t[]=new char [key.length()];
t=key.toCharArray();
Arrays.sort(t);
for (int j=0;j<y;j++)
{
for (int i=0;i<key.length();i++)
{
System.out.print(temp[i][j]);
}
System.out.println();
}
System.out.println();
// To print out row by row (i.e. y)
for (int j=0;j<y;j++){
// To compare the the sorted Key with the key
// For char in the key
for (int i=0;i<key.length();i++){
int pos=0;
// To get the position of key.charAt(i) from sorted key
for (pos=0;pos<t.length;pos++){
if (key.charAt(i)==t[pos]){
// To break the for loop once the key is found
break;
}
}
System.out.print(temp[pos][j]);
encryptedMessage+=temp[pos][j];
}
System.out.println();
}
System.out.println(encryptedMessage);
System.exit(0);
}enter code here
}
Take the output of one operation (PlayfairCipher.encode), which is the cyphertext and input it to the second operation (your transpositionial code) as it's plaintext.
Scanner sc = new Scanner(System.in);
String key = prompt("Enter an encryption key (min length 6): ", sc, 6);
String txt = prompt("Enter the message: ", sc, 1);
String jti = prompt("Replace J with I? y/n: ", sc, 1);
boolean changeJtoI = jti.equalsIgnoreCase("y");
PlayfairCipher.createTable(key, changeJtoI);
String enc = PlayfairCipher.encode(prepareText(txt, changeJtoI));
//Now instead of using 'message' for the second encryption, you use the output of the first operation 'enc'
int x=0;
int y=0;
int msgchar = enc.length();
int keycahr = key.length();
if (!((msgchar % keycahr) == 0)){
do{
enc = enc + "x";
msgchar = enc.length();
}while(!((msgchar % keycahr) == 0));
}
...
Descriptions: I am suppose to come up with a program that will break encrypted messages that used a Caesar Cipher algorithm. Sounds easy enough the problem is you have to figure out what position was used to make the encrypted message in order to decrypt the coded message.
So I have a method called public void train(String trainingFileName) that reads in a file with a lot of text in it to determine the frequency of each lowercase alphabetical English character (a - z) that are stored in a double array []. That method works great so it's not necessary to look at unless you want to, but I do use a good majority of that code in the method that I'm having trouble on which is my public int decrypt(String cipherTextFileName, String outputFileName) method.
The code works beautifully when the Caesar Cipher is set to 3 positions, but anything else it gives me a lot of problems.
I have a do-while loop in my public int decrypt(String cipherTextFileName, String outputFileName) method that will decrypt the coded message starting with 0 positions and then using the "distance" formula that I'm using (NOTE: I cannot use any other formula) to find the minimum distance between the knownFrequencies and the observedFreq in my encrypted message. Right now I have my do-while loop set to where if the distance is less than 0.6 then stop the loop. In theory when I have the correct number of positions in the Caesar Cipher the distance should be below that value.
Problem: Program works great when numberOfPositions is 3, but when I use an encrypted message that is not using 3 positions in the Caesar Cipher the distance never falls below 1 and while in debug mode when I set the numberOfPositions to what it should be to decrypt the message, the message is still encrypted.
Question: How can I implement this method better so I am not testing my distance on a "hard" value to stop the do-while loop? I tried using Math.min(), but that doesn't work. Why can't I decode a message with the positions of the Caesar Cipher other than 3.
I will show you my code now. If you want to test it on your system. You will need 3 text files. One file has to be long with a bunch of words in it... at least 1000. That file will be read in the train method. You need a file with an encrypted message and another file for the program to write the decrypted message.
Here is an encrypted message first using 3 positions of the Caesar Cipher and then 5 positions.
Wkh surjudp zdv krvwhg eb dfwru Slhufh Eurvqdq dqg kdg frpphqwdub iurp pdqb Kroobzrrg dfwruv dqg iloppdnhuv Prylh txrwdwlrqv wkdw ylhzhuv xvh lq wkhlu rzq olyhv dqg vlwxdwlrqv
Ymj uwtlwfr bfx mtxyji gd fhytw Unjwhj Gwtxsfs fsi mfi htrrjsyfwd kwtr rfsd Mtqqdbtti fhytwx fsi knqrrfpjwx Rtanj vztyfyntsx ymfy anjbjwx zxj ns ymjnw tbs qnajx fsi xnyzfyntsx
When decrypted it should say:
The program was hosted by actor Pierce Brosnan and had commentary from many Hollywood actors and filmmakers Movie quotations that viewers use in their own lives and situations
Alright here is the class I wrote (you will need all the imports) and I would like to thank anyone who helps in advance:
public class CodeBreaker {
public final int NUMBER_OF_LETTERS = 26;
private double[] knownFrequencies = new double[NUMBER_OF_LETTERS];
public double[] getKnownFrequencies() {
return knownFrequencies;
}
public void setKnownFrequencies(double[] knownFrequencies) {
this.knownFrequencies = knownFrequencies;
}
/**
* Method reads in a file with a lot of text in it and
* then use that to figure out the frequencies of each character
*
* #param trainingFileName
*/
public void train(String trainingFileName) {
try {
Scanner fileIO = new Scanner(new File(trainingFileName));
int total = 0;
String temp = "";
while (fileIO.hasNext()) {
//reading into file and storing it into a string called temp
temp += fileIO.next().toLowerCase().replaceAll("[ -,!?';:.]+", "");
//converting temp string into a char array
}
char[] c = temp.toCharArray();
total += c.length; // how many characters are in text
int k = (int) 'a'; // int value of lowercase letter 'a'
int[] counter = new int[NUMBER_OF_LETTERS];
for (int j = 0; j < total; j++) {
for (int i = k - k; i < knownFrequencies.length; i++) {
char[] d = new char[knownFrequencies.length];
d[i] = (char) (k + i);
if (c[j] == d[i]) {//checking to see if char in text equals char in d array
counter[i]++;
knownFrequencies[i] = (double) counter[i] / total;
}
}
}
fileIO.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
System.err.println(e);
System.exit(0);
}
}
/**
* Main decryption method used to take coded text from a file, figure out the positions in the CaesarCipher
* and then decode it onto another file.
*
* #param cipherTextFileName
* #param outputFileName
* #return
*/
public int decrypt(String cipherTextFileName, String outputFileName) {
Scanner fileIO;
int numberOfPositions = 0;
double distance = 0.000000;
try {
fileIO = new Scanner(new File(cipherTextFileName));
PrintWriter writer = new PrintWriter(new File(outputFileName));
String temp = "";
while (fileIO.hasNext()) {
//reading into file and storing it into a string called temp
temp += fileIO.next().toLowerCase().replaceAll(" ", "");
}
fileIO.close();
do {
distance = 0.0;
int total = 0;
double[] observedFreq = new double[NUMBER_OF_LETTERS];
temp = decrypt(temp, numberOfPositions);
char[] c = temp.toCharArray(); //store decrypted chars into an array
total += c.length; // how many characters are in text
int k = (int) 'a'; // int value of lowercase letter 'a'
int[] counter = new int[NUMBER_OF_LETTERS]; //use to count the number of characters in text
for (int j = 0; j < total; j++) {
for (int i = k - k; i < observedFreq.length; i++) {
char[] d = new char[observedFreq.length];
d[i] = (char) (k + i);
if (c[j] == d[i]) { //checking to see if char in text equals char in d array
counter[i]++;
observedFreq[i] = (double) counter[i] / total;
}
}
}
//Formula for finding distance that will determine the numberOfPositions in CaesarCipher
for (int j = 0; j < knownFrequencies.length; j++) {
distance += Math.abs(knownFrequencies[j] - observedFreq[j]); //This is the part of the code I am having trouble with
}
numberOfPositions = numberOfPositions + 1;
} while (distance > 0.6); //This is the part of the code I am having trouble with
Scanner fileIO2 = new Scanner(new File(cipherTextFileName));
while (fileIO2.hasNextLine()) {
//reading into file and storing it into a string called temp
temp = fileIO2.nextLine();
writer.println(decrypt(temp, numberOfPositions));
}
writer.close();
fileIO2.close();
System.out.println(distance);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
System.err.println(e);
System.exit(0);
}
return numberOfPositions;
}
/**
* CaesarCipher decrypt and encrypt methods
*
* #param ciphertext
* #param numberOfPositions
* #return
*/
public String decrypt(String ciphertext, int numberOfPositions) {
// TODO Auto-generated method stub
return encrypt(ciphertext, -numberOfPositions);
}
public String encrypt(String msg, int offset) {
offset = offset % 26 + 26;
StringBuilder encoded = new StringBuilder();
for (char i : msg.toCharArray()) {
if (Character.isLowerCase(i)) {
int j = (i - 'a' + offset) % 26;
encoded.append((char) (j + 'a'));
} else if (Character.isUpperCase(i)) {
int h = (i - 'A' + offset) % 26;
encoded.append((char) (h + 'A'));
} else {
encoded.append(i);
}
}
return encoded.toString();
}
// barebones main method to test your code
public static void main(String[] args) {
// args[0] contains the filename of the training file
// args[1] contains the filename of the cipher text file
// args[2] contains the filename of the output file
CodeBreaker cb = new CodeBreaker();
cb.train(args[0]);
System.out.println(cb.decrypt(args[1], args[2]));
}
}
The standard method of decoding a Caesar cypher is called, "running down the alphabet". Essentially a brute force solution; you try all the possibilities. Since there are only 26 possible keys, it is not that difficult.
Taking your example:
WKH SURJUDP ZDV KRVWHG ...
wkh surjudp zdv krvwhg ...
xli tvskveq aew lswxih ...
ymj uwtlwfr bfx mtxyji ...
znk vxumxgs cgy nuyzkj ...
aol wyvnyht dhz ovzalk ...
bpm xzwoziu eia pwabml ...
cqn yaxpajv fjb qxbcnm ...
dro zbyqbkw gkc rycdon ...
esp aczrclx hld szdepo ...
ftq bdasdmy ime taefqp ...
gur cebtenz jnf ubfgrq ...
hvs dfcufoa kog vcghsr ...
iwt egdvgpb lph wdhits ...
jxu fhewhqc mqi xeijut ...
kyv gifxird nrj yfjkvu ...
lzw hjgyjse osk zgklwv ...
max ikhzktf ptl ahlmxw ...
nby jlialug qum bimnyx ...
ocz kmjbmvh rvn cjnozy ...
pda lnkcnwi swo dkopaz ...
qeb moldoxj txp elpqba ...
rfc npmepyk uyq fmqrcb ...
sgd oqnfqzl vzr gnrsdc ...
the program was hosted ...
uif qsphsbn xbt iptufe ...
vjg rtqitco ycu jquvgf ...
wkh surjudp zdv krvwhg ...
It is simple enough for a human to find the correct line with only 26 to pick from. For a computer it is more difficult. Your idea of counting letter frequencies is good. You might also mark down letter pairs like "qx" and mark up pairs like "th". Calculate the score for all 26 possible results and pick the highest scoring result. As long as you have tuned your scoring method well, then you have a good chance of finding the right solution.
Taking the suggestion from rossum, and realizing that my initial class was a total mess that nobody could understand. I rewrote the class using bunch of methods this time instead of clumping everything into one or two methods, and now the class works perfectly. I am up for any suggestions to make the code more efficient. To me it seems a little redundant so any suggestion for improvement are welcome. This was for a class assignment which the due date has passed, so this code is going to be for reference.
public class CodeBreaker {
//Setting up instance variables and setter/getter methods
public final int NUMBER_OF_LETTERS = 26;
private int numberOfPositions = 0;
private double[] knownFrequencies = new double[NUMBER_OF_LETTERS];
private double[] observedFreq = new double[NUMBER_OF_LETTERS];
public double[] getKnownFrequencies() {
return knownFrequencies;
}
public void setKnownFrequencies(double[] knownFrequencies) {
this.knownFrequencies = knownFrequencies;
}
//This method reads text from a long file, breaks it down into individual characters, and stores it in the knownFrequencies array
public void train(String trainingFileName) {
String tempString = "";
double totalChars = 0.0;
try {
#SuppressWarnings("resource")
Scanner FileIO = new Scanner(new File(trainingFileName)).useDelimiter("[ *-,!?.]+"); //reading text from a file using
//the delimiter so we get all of the contents
while(FileIO.hasNext()){
tempString += FileIO.next().toLowerCase();//storing contents into a string, all lower case
}
FileIO.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
System.err.println(e);
System.exit(0);
}
//Figuring out total number of English letters(a-z) used to determine the frequencies
for(int j = 0; j < tempString.length(); j++){
char ch = tempString.charAt(j);
if(Character.isAlphabetic(ch)){
totalChars++;
}
}
//Initializing the knownFrequencies array with each individual letter count a-z
for (int k = 0; k <= tempString.length()-1; k++){
char ch = tempString.charAt(k);
double chValue = (double) ch;
if (Character.isAlphabetic(ch)) {
if(chValue >= 97 && chValue <= 122){
knownFrequencies[ch - 'a']++;
}
}
}
//Divide the individual letter counts by the total to get a decimal number
//for the frequency and store that into the knownFrequencies array.
for (int i = 0; i < knownFrequencies.length; i++) {
if(knownFrequencies[i] > 0){
knownFrequencies[i] = knownFrequencies[i]/totalChars;
}
}
}
//This method does practically the same thing in the train method except it doesn't read from a file, and it compiles all of the
//cipher text characters to find the frequencies that will be used later to determine the key
public void setObservedFreq(String tempString)//String parameter takes in the cipher text
{
//Finding total number of lower case English letters (a-z)
double totalChars = 0.0;
for(int j = 0; j < tempString.length(); j++){
char ch = tempString.charAt(j);
if(Character.isAlphabetic(ch)){
totalChars++;
}
}
//Initializing observedFreq with the number of letters in the string.
for (int k = 0; k <= tempString.length()-1; k++){
char ch = tempString.charAt(k);
double chValue = (double) ch;
if (Character.isAlphabetic(ch)) {
if(chValue >= 97 && chValue <= 122){
observedFreq[ch - 'a']++;
}
}
}
//Re-initializing with a decimal frequency.
for (int i = 0; i < NUMBER_OF_LETTERS; i++) {
if(observedFreq[i] > 0){
observedFreq[i] = observedFreq[i]/totalChars;
}
}
}
//This method subtracts the absolute value of the observedFreq from the knownFrequencies, sum all those together and store it
//in a variable that will be return in the method. The smallest distance value means the cipher text has been decoded.
public double findDistance(){
double distance = 0.0;
for(int x = 0; x < NUMBER_OF_LETTERS; x++){
distance += Math.abs(knownFrequencies[x] - observedFreq[x]);
}
return(distance);
}
//This method finds a int value that will be used as the key to decipher the cipherText
public int findNumberOfPositions(String cipherText){
int smallestIndex = 0;
double [] indexArray = new double [NUMBER_OF_LETTERS];
//We are going through all possible shifts (up to 25) to see and storing those distances into the indexArray.
for(int i = 0; i < NUMBER_OF_LETTERS; i ++){
setObservedFreq(decrypt(cipherText,i));
indexArray[i] = findDistance();
}
//Determine which index in the array has the smallest distance
double currentValue = indexArray[0];
for (int j=0; j < NUMBER_OF_LETTERS; j++) {
if (indexArray[j] < currentValue)
{
currentValue = indexArray[j];
smallestIndex = j;
}
}
return smallestIndex; //The index is returned and will be used for the key when the message is decrypted
}
//Read in a file that contains cipher text decrypt it using the key that was found in the findNumberOfPositions method
//then write the plain text into a output file.
public int decrypt(String cipherTextFileName, String outputFileName) {
String tempString = "";
try {
#SuppressWarnings("resource")
Scanner FileIO = new Scanner(new File(cipherTextFileName)).useDelimiter("[ *-,!?.]+");
while(FileIO.hasNext()){
tempString += FileIO.next().toLowerCase();//read into a file and store lower case text it into tempString
}
FileIO.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
System.err.println(e);
System.exit(0);
}
numberOfPositions = findNumberOfPositions(tempString); //call our findNumberOfPositions method to find the key
try {
Scanner scan = new Scanner(new File(cipherTextFileName));
PrintWriter writer = new PrintWriter(new File(outputFileName));
while(scan.hasNextLine()){
writer.println(decrypt(scan.nextLine(), numberOfPositions)); //key is then used to decrypt the message and gets
//printed into another file.
}
writer.close();
scan.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
System.err.println(e);
System.exit(0);
}
return numberOfPositions;
}
//Caesar Cipher encrypt and decrypt methods
public String decrypt(String ciphertext, int numberOfPositions) {
// TODO Auto-generated method stub
return encrypt(ciphertext, -numberOfPositions);
}
public String encrypt(String msg, int offset){
offset = offset % 26 + 26;
StringBuilder encoded = new StringBuilder();
for (char i : msg.toCharArray()) {
if (Character.isLowerCase(i)) {
int j = (i - 'a' + offset) % 26;
encoded.append((char) (j + 'a'));
}
else if(Character.isUpperCase(i)){
int h = (i - 'A' + offset) % 26;
encoded.append((char) (h + 'A'));
}
else {
encoded.append(i);
}
}
return encoded.toString();
}
public static void main(String[] args) {
// args[0] contains the filename of the training file
// args[1] contains the filename of the cipher text file
// args[2] contains the filename of the output file
CodeBreaker cb = new CodeBreaker();
cb.train(args[0]);
cb.decrypt(args[1], args[2]);
}
}