I am new in JAVA Programming and I am trying to create a caesar cipher encrypt/decrypt program. Unfortunately, my program is only working in lower cases. I cannot see where did I go wrong I tried reviewing my code for several times but I cant seem to find out the problem. Here is my code so far:
import java.util.Scanner;
public class CaesarCipher {
public static String encrypt(String plainText, int shift) {
if (shift > 26) {
shift = shift % 26;
} else if (shift < 0) {
shift = (shift % 26) + 26;
}
String cipherText = "";
int length = plainText.length();
for (int i = 0; i < length; i++) {
char ch = plainText.charAt(i);
if (Character.isLetter(ch)) {
if (Character.isLowerCase(ch)) {
char c = (char) (ch + shift);
if (c > 'z') {
cipherText += (char) (ch - (26 - shift));
} else {
cipherText += c;
}
} else if (Character.isUpperCase(ch)) {
char c = (char) (ch + shift);
if (c > 'Z') {
cipherText += (char) (ch - (26 - shift));
} else {
cipherText += c;
}
}
} else {
cipherText += ch;
}
}
return cipherText;
}
// Decrypt
public static String decrypt(String plainText, int shift) {
if (shift > 26) {
shift = shift % 26;
} else if (shift < 0) {
shift = (shift % 26) + 26;
}
String cipherText = "";
int length = plainText.length();
for (int i = 0; i < length; i++) {
char ch = plainText.charAt(i);
if (Character.isLetter(ch)) {
if (Character.isLowerCase(ch)) {
char c = (char) (ch - shift);
if (c < 'a') {
cipherText += (char) (ch + (26 - shift));
} else {
cipherText += c;
}
} else if (Character.isUpperCase(ch)) {
char c = (char) (ch + shift);
if (c < 'A') {
cipherText += (char) (ch + (26 - shift));
} else {
cipherText += c;
}
}
} else {
cipherText += ch;
}
}
return cipherText;
}
public static void main(String[] args) {
Scanner input1 = new Scanner(System.in);
Scanner input2 = new Scanner(System.in);
System.out.print("Enter your phrase: ");
String inputPlainText = input1.nextLine();
System.out.print("Enter your shift: ");
int shiftForPlainText = input1.nextInt();
String convertPlainText = encrypt(inputPlainText, shiftForPlainText);
System.out.println(convertPlainText);
System.out.print("Enter ciphertext: ");
String inputCipherText = input2.nextLine();
System.out.print("Enter shift: ");
int shiftForCipherText = input2.nextInt();
String convertCipherText = decrypt(inputCipherText, shiftForCipherText);
System.out.println(convertCipherText);
}
}
In decrypt method, for lower case you write:
char c = (char)(ch-shift);
and for upper case you write:
char c = (char)(ch+shift);
I'm pretty sure that both lines should have the same operator between ch and shift. If you don't want to make such mistakes, try to refactor your code so there are no duplicate lines.
Related
I'm just a beginning programmer, and I'm here to find a bug in my program. The program only reads the uppercase letters in my text file, even though I have a lowercase case in my encrypt and decrypt methods. I'm guessing it's a problem with the caesarEncipher method. (Ignore my Decipher case in the main, I will get to it soon.)
import java.util.*;
import java.io.*;
public class Cipher {
public static void main(String[] args) throws FileNotFoundException {
Scanner scan = new Scanner(System.in);
System.out.println("Welcome to CaesarCipher");
System.out.println();
System.out.println("Enter 1 to Encipher, 2 to Decipher, or -1 to exit");
int choice = 0;
do {
choice = scan.nextInt();
if (choice == 1) {
System.out.println("What non-negative shift should be used?");
int shift = scan.nextInt();
System.out.println("What is the input file name?");
String input = scan.next();
System.out.println("What is the output file name?");
String output = scan.next();
System.out.println(caesarEncipher(input, shift, output));
} else if (choice == 2) {
} else if (choice == -1) {
System.out.println("Thank you for using CaesarCipher");
break;
}
} while (choice != 1 && choice != 2 && choice != -1);
}
public static String caesarEncipher(String inputString, int shift, String output) throws FileNotFoundException {
File outFile = new File(output);
PrintStream encoded = new PrintStream(outFile); // creates new file for the output
File input = new File(inputString); // creates file with String to scan
Scanner scan = new Scanner(input); // creates Scanner
while (scan.hasNextLine()) {
String cipher = scan.nextLine(); // gets next line of file
String encipher = ""; // String to be added to new file
int i;
for (i = 0; i < cipher.length(); i++) {
String curr = cipher.substring(i, i + 1); // current character
String newChar = encrypt(curr, shift);
encipher = encipher + newChar;
}
encoded.println(encipher);
}
encoded.close();
return "DONE";
}
public static String encrypt(String str, int shift) {
String encrypted = "";
for (int i = 0; i < 1; i++) {
int c = str.charAt(i);
if (Character.isUpperCase(c)) {//if uppercase
c = c + (shift % 26);
if (c > 'Z') { //resets if it passes 'Z'
c = c - 26;
} else if (Character.isLowerCase(c)) {// if lowercase
c = c + (shift % 26);
if (c > 'z') { // resets if it passes 'z'
c = c - 26;
}
}
encrypted = encrypted + (char) c; // adds the encrypted character to the string
}
}
return encrypted;
}
public static String decrypt(String str, int shift) {
String decrypted = "";
for (int i = 0; i < 1; i++) {
int c = str.charAt(i);
if (Character.isUpperCase(c)) //if uppercase
{
c = c + (shift % 26);
if (c < 'A') { //resets if it passes 'A'
c = c + 26;
}
} else if (Character.isLowerCase(c)) // if lowercase
{
c = c + (shift % 26);
if (c < 'a') { // resets if it passes 'a'
c = c + 26;
}
}
decrypted = decrypted + (char) c; // adds the derypted character to the string
}
return decrypted;
}
}
In encrypt() method: This part of code
else if(Character.isLowerCase(c)) {
c=c+(shift%26);
if(c>'z') {
c=c-26;
}
}
encrypted=encrypted+(char)c;
belongs to if(c>'Z') instead of if(Character.isUpperCase(c))
For my CPSC class, I need to make encryption code using caesar cipher. That is done. The next part is taking the encrypted message and cycling the secretKey to be added to the encrypted message. For example, if I encrypt "Hello!" using a shift of 13, it will turn into "Uryyb!". Then I must shift "U" by one, "r" by three, "y" by one, etc... which will encrpt into "Vuzbc!" I am in a beginner class so I do not know all the cool tips and tricks. Only possible solution I know is to take the outcome of the caesar cipher and somehow index the secret key to be added to the outcome.
Here is my code that I have so far:
public class Cipher {
private int secretKey;
private int superSecretKey;
public static void main(String [] args)
{
Cipher cipher = new Cipher(1);
}
public Cipher(int myKey) {
secretKey = myKey;
}
public String caesarEncrpyt (String s) {
String r = "";
for(int i = 0; i < s.length(); i++) {
char c = (char) (s.charAt(i));
if(Character.isLetter(c)) {
if (Character.isUpperCase(c)) {
r += (char) ('A' + (c - 'A' + secretKey) % 26);
}
else {
r += (char) ('a' + (c - 'a' + secretKey) % 26);
}
}
else {
r += c;
}
}
return r;
}
public String caesarDecrypt (String s) {
String r = "";
for(int i = 0; i < s.length(); i++) {
char c = (char) (s.charAt(i));
if(Character.isLetter(c)) {
if (Character.isUpperCase(c)) {
r += (char) ('A' + (c - 'A' - secretKey) % 26);
}
else {
r += (char) ('a' + (c - 'a' - secretKey) % 26);
}
}
else {
r += c;
}
}
return r;
}
public String augustusEncrypt (String s) {
String r = "";
for(int i = 0; i < s.length(); i++) {
char c = (char) (s.charAt(i));
if(Character.isLetter(c)) {
if (Character.isUpperCase(c)) {
r += (char) ('A' + (c - 'A' + secretKey) % 26);
}
else {
r += (char) ('a' + (c - 'a' + secretKey) % 26);
}
}
else {
r += c;
}
}
return r;
}
augustusEncrypt is a copy and paste of caesarEncrypt. I've been moving some stuff around hoping for a solution. Thanks in advance!
Edit: I may not have explained this correctly, if you have a question, I'll be here.
Write a function, call it toDigits which will take an int (or a long) and return an array of ints corresponding to the digits of the input. to toDigits(13)=>{1,3} and toDigits(4834)=>{4,8,3,4}, etc
Then write a function encryptChar, taking a char and an int and encrypting the char by that int. (encryptChar('e', 1)=>'f', encryptChar('a',28)=>c, etc)
Then you can loop over the characters of the message and the digits in this array, passing the values to encryptChar and use the results to assemble your encrypted message. In a loose sort of pseudocode:
fn encryptMessage(message, key):
key_array = toDigits(key)
output = ""
for i in length (message):
output.append(encryptChar(message[i], key_array[i % length(key_array)]))
Best practices and conventions for encrypting aside, the solution is simple.
You have letters A-Z and a-z which already perform the correct loop when we step off of the alphabet, and you believe you have that working correctly. All you need to do is add 1 before you loop around.
It would be something like this (warning: untested):
('A' + ((c+1) - 'A' + secretKey) % 26)
So given the encrypt code
// encrypt looks up the character at the appropriate place
// in the encryption String and substitutes it.
public void encrypt (StringBuilder text)
{
for (int i=0; i<text.length(); i++)
{
char ch = text.charAt(i);
if ('A' <= ch && ch <= 'Z')
{
int index = ALPHABET.indexOf(ch);
text.setCharAt(i, encryptionString.charAt(index));
}
}
}
How would I modify the above code to make it so it decrypts?
public class SubstitutionCipher
{
// The alphabet as a String. We use this for both
// encoding and decoding
public static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// The encryption string is how we represent the table
// of values. The first character in the String
// is substituted for 'A', the second for 'B', etc.
private String encryptionString;
/**
* Constructor for objects of class Substitution
*/
public SubstitutionCipher(String substitutionString)
{
if (substitutionString.length() != 26)
{
System.out.println ("Illegal substitution string "
+ substitutionString);
encryptionString = ALPHABET;
}
else
{
encryptionString = substitutionString;
}
}
// encrypt looks up the character at the appropriate place
// in the encryption String and substitutes it.
public void encrypt (StringBuilder text)
{
for (int i=0; i<text.length(); i++)
{
char ch = text.charAt(i);
if ('A' <= ch && ch <= 'Z')
{
int index = ALPHABET.indexOf(ch);
text.setCharAt(i, encryptionString.charAt(index));
}
}
}
// decrypt looks up the character at the appropriate place
// in the alphabet and substitutes it.
public void decrypt (StringBuilder text)
{
for (int i=0; i<text.length(); i++)
{
char ch = text.charAt(i);
if ('A' <= ch && ch <= 'Z')
{
int index = ALPHABET.indexOf(ch);
text.setCharAt(i, encryptionString.charAt(index));
}
}
}
}
In decryption you have to do just reverse of encryption, But in your code, you are doing the same thing, So change it to this :
public void decrypt (StringBuilder text)
{
for (int i=0; i<text.length(); i++)
{
char ch = text.charAt(i);
if ('A' <= ch && ch <= 'Z')
{
int index =encryptionString.indexOf(ch);
text.setCharAt(i, ALPHABET.charAt(index));
}
}
}
Run Length Encoding is taking a string with reoccurring characters, for example qqqqqWWWWr, and transforming it to 5q4Wr. The reoccurring character should be preceded with the amount of times it reoccurs consecutively. If it only occurs once in a row it should not be preceded by a number.
Here is what I have so far, and I cannot seem to get it to work properly:
public class Compress {
public static void main(String[] args) {
System.out.print("Enter a string: ");
String s = IO.readString();
int currentRunLength = 1;
String compressedString = "";
for (int i = 0; i < s.length(); i++) {
if (i == s.length() - 1){
if (currentRunLength == 1) {
compressedString += s.charAt(i);
break;
} else {
compressedString += currentRunLength + s.charAt(i);
break;
}
}
if (s.charAt(i) == s.charAt(i + 1)) {
currentRunLength++;
}
if (s.charAt(i) != s.charAt(i + 1) && currentRunLength > 1) {
compressedString += currentRunLength + s.charAt(i);
currentRunLength = 1;
}
if (s.charAt(i) != s.charAt(i + 1) && currentRunLength == 1) {
compressedString += s.charAt(i);
}
}
IO.outputStringAnswer(compressedString);
}
}
Example run
Enter a string: qqqqWWWr
RESULT: "117q90Wr"
Maybe you shouldn't reinvent the wheel when it's a decently-known algorithm. I found an implementation on Rosetta Code at the following URL: http://rosettacode.org/wiki/Run-length_encoding#Java
Re-pasting in case the original URL is removed:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RunLengthEncoding {
public static String encode(String source) {
StringBuffer dest = new StringBuffer();
for (int i = 0; i < source.length(); i++) {
int runLength = 1;
while (i+1 < source.length() && source.charAt(i) == source.charAt(i+1)) {
runLength++;
i++;
}
dest.append(runLength);
dest.append(source.charAt(i));
}
return dest.toString();
}
public static String decode(String source) {
StringBuffer dest = new StringBuffer();
Pattern pattern = Pattern.compile("[0-9]+|[a-zA-Z]");
Matcher matcher = pattern.matcher(source);
while (matcher.find()) {
int number = Integer.parseInt(matcher.group());
matcher.find();
while (number-- != 0) {
dest.append(matcher.group());
}
}
return dest.toString();
}
public static void main(String[] args) {
String example = "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWBWWWWWWWWWWWWWW";
System.out.println(encode(example));
System.out.println(decode("1W1B1W1B1W1B1W1B1W1B1W1B1W1B"));
}
}
The first problem I see is on this line:
compressedString += currentRunLength + s.charAt(i);
You are adding an int to a char. The char is cast to int before addition, and then the result from the addition is appended to your String. The ASCII value of q is 113. 113 + 4 would explain the beginning of your String -> 117.
The problem is related to this line:
compressedString += currentRunLength + s.charAt(i)
What this is doing is taking int and adding it to char (which will result in an int), then adding it to the compressed string.
You can address that with this minor change, which will convert currentRunLength to a String, then append the char:
compressedString += String.valueOf(currentRunLength) + s.charAt(i)
I have this simple function for encrypting strings via Vigenère in Java. I omitted the decryption as this is just a "-" instead of the "+" in the line where the new value is calculated.
But this function works only for the normal alphabet A-Z. How can I change the function so that it supports lowercase letters as well as uppercase letters and all other UTF-8 chars?
public static String vigenere_encrypt(String plaintext, String key) {
String encryptedText = "";
for (int i = 0, j = 0; i < plaintext.length(); i++, j++) {
if (j == key.length()) { j = 0; } // use key again if end reached
encryptedText += (char) ((plaintext.charAt(i)+key.charAt(j)-130)%26 + 65);
}
return encryptedText;
}
Thank you very much for your help!
Well, you asked for it and I felt like puzzling, but print out the cipher text and you will know what you just asked for...
public static String vigenereUNICODE(String plaintext, String key, boolean encrypt) {
final int textSize = plaintext.length();
final int keySize = key.length();
final StringBuilder encryptedText = new StringBuilder(textSize);
for (int i = 0; i < textSize; i++) {
final int plainNR = plaintext.codePointAt(i);
final int keyNR = key.codePointAt(i % keySize);
final long cipherNR;
if (encrypt) {
cipherNR = ((long) plainNR + (long) keyNR) & 0xFFFFFFFFL;
} else {
cipherNR = ((long) plainNR - (long) keyNR) & 0xFFFFFFFFL;
}
encryptedText.appendCodePoint((int) cipherNR);
}
return encryptedText.toString();
}
EDIT: Please don't ever use in production code, as I haven't got a clue if the code points can indeed be encoded/decoded. Not all points have been defined, as far as I know, and the standard is a moving target.
If full unicode support is not possible and you have to define your list of valid characters, anyway, why not just use a function like this?
public static String vigenere_cipher(String plaintext, String key, boolean encrypt) {
String alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ,.-"; // including some special chars
final int alphabetSize = alphabet.length();
final int textSize = plaintext.length();
final int keySize = key.length();
final StringBuilder encryptedText = new StringBuilder(textSize);
for (int i = 0; i < textSize; i++) {
final char plainChar = plaintext.charAt(i); // get the current character to be shifted
final char keyChar = key.charAt(i % keySize); // use key again if the end is reached
final int plainPos = alphabet.indexOf(plainChar); // plain character's position in alphabet string
if (plainPos == -1) { // if character not in alphabet just append unshifted one to the result text
encryptedText.append(plainChar);
}
else { // if character is in alphabet shift it and append the new character to the result text
final int keyPos = alphabet.indexOf(keyChar); // key character's position in alphabet string
if (encrypt) { // encrypt the input text
encryptedText.append(alphabet.charAt((plainPos+keyPos) % alphabetSize));
}
else { // decrypt the input text
int shiftedPos = plainPos-keyPos;
if (shiftedPos < 0) { // negative numbers cannot be handled with modulo
shiftedPos += alphabetSize;
}
encryptedText.append(alphabet.charAt(shiftedPos));
}
}
}
return encryptedText.toString();
}
This should be a very short and working version. And the alphabet can easily be stored in a string that can always be extended (which results in different ciphertexts).
Another answer, that does do the Vigenere cipher on upper & lower case characters, simply inserting the other characters. Use this technique to create multiple groups of characters to encode.
public static String vigenere(String plaintext, String key, boolean encrypt) {
final int textSize = plaintext.length();
final int keySize = key.length();
final int groupSize1 = 'Z' - 'A' + 1;
final int groupSize2 = 'z' - 'a' + 1;
final int totalGroupSize = groupSize1 + groupSize2;
final StringBuilder encryptedText = new StringBuilder(textSize);
for (int i = 0; i < textSize; i++) {
final char plainChar = plaintext.charAt(i);
// this should be a method, called for both the plain text as well as the key
final int plainGroupNumber;
if (plainChar >= 'A' && plainChar <= 'Z') {
plainGroupNumber = plainChar - 'A';
} else if (plainChar >= 'a' && plainChar <= 'z') {
plainGroupNumber = groupSize1 + plainChar - 'a';
} else {
// simply leave spaces and other characters
encryptedText.append(plainChar);
continue;
}
final char keyChar = key.charAt(i % keySize);
final int keyGroupNumber;
if (keyChar >= 'A' && keyChar <= 'Z') {
keyGroupNumber = keyChar - 'A';
} else if (keyChar >= 'a' && keyChar <= 'z') {
keyGroupNumber = groupSize1 + keyChar - 'a';
} else {
throw new IllegalStateException("Invalid character in key");
}
// this should be a separate method
final int cipherGroupNumber;
if (encrypt) {
cipherGroupNumber = (plainGroupNumber + keyGroupNumber) % totalGroupSize;
} else {
// some code to go around the awkward way of handling % in Java for negative numbers
final int someCipherGroupNumber = plainGroupNumber - keyGroupNumber;
if (someCipherGroupNumber < 0) {
cipherGroupNumber = (someCipherGroupNumber + totalGroupSize);
} else {
cipherGroupNumber = someCipherGroupNumber;
}
}
// this should be a separate method
final char cipherChar;
if (cipherGroupNumber < groupSize1) {
cipherChar = (char) ('A' + cipherGroupNumber);
} else {
cipherChar = (char) ('a' + cipherGroupNumber - groupSize1);
}
encryptedText.append(cipherChar);
}
return encryptedText.toString();
}
Again, this is unsafe code as the cipher used has been broken for ages. Don't use too many 'A' characters in your keys :) But the character encoding should be sound.