Caesar cipher by comparing char array with two arrays - java

I need to build a Caesar cipher that only encrypts letters, but no special characters. My concept was, to compare the input char[] with two alphabet char[]. If there is no match in a char, the char should be added to the String without being changed. The problem is that the not-changed char will be added to the String until the the for-loop ends. How do I fix this?
public static String encrypt(String text, int number) {
String str = "";
char[] chars = text.toCharArray();
char[] al = "abcdefghijklmnopqrstuvwxyz".toCharArray();
char[] ab = "abcdefghijklmnopqrstuvwxyz".toUpperCase().toCharArray();
for (char c : chars) {
boolean match = false;
for (int i = 1; i < chars.length - 1; i++) {
for (int k = 0; (k < al.length || k < ab.length) && !match; k++) {
match = (c == al[k] || c == ab[k]);
if (match) {
c += number;
str += c;
}
}
if (!match) {
str += c;
}
}
}
return str;
}
I already tried to put the case for not changing the string within the other for-loop, but it will be added until the for-loop has reached it's end.

I would tackle the problem by iterating through the String and considering the possible cases for each letter
Uppercase Letter
Lowercase Letter
Special Character
public static String encrypt(String text, int number) {
//String to hold our return value
String toReturn = "";
//Iterate across the string at each character
for (char c : text.toCharArray()){
if (Character.isUpperCase(c)){
/* If uppercase, add number to the character
If the character plus number is more than 90,
subtract 25 [uppercase letters have ASCII 65 to 90] */
toReturn += c + number > 90 ? (char)(c + number - 25) : (char)(c + number);
} else if (Character.isLowerCase(c)){
/* If lowercase, add number to the character
If the character plus number is more than 122,
subtract 25 [uppercase letters have ASCII 97 to 122] */
toReturn += c + number > 122 ? (char)(c + number - 25) : (char)(c + number);
} else {
// For other characters, just add it onto the return string
toReturn += c;
}
}
return toReturn;
}
Explanation of Code
You might be wondering what the following code does
toReturn += c + number > 90 ? (char)(c + number - 25) : (char)(c + number)
The structure is
toReturn += CONDITION ? A : B
It basically reads as
IF CONDITION IS TRUE, toReturn += A, ELSE toReturn += B
The CONDITION is simply c + number > 90 since we want to make sure that we are sticking with uppercase letters only
When this is true (A), we subtract 25 from c + number, otherwise (B) we just keep it as c + number (B)
We then cast this value into a char since it is initially an int

Related

Caesar Cipher Getting Wrong Output from Input Cases

I am using StringBuilder to change a String input and shift it depending on input. This is for the META coding practice website and I am running into an issue with their two Test cases. One is passing and the other is not.
The expected output is stuvRPQrpq-999.#and the input is abcdZXYzxy-999.# with a shift of 200.
Here is my code
String rotationalCipher(String input, int rotationFactor) {
// Write your code here
int shift = rotationFactor % 26;
StringBuilder output = new StringBuilder();
for (char character : input.toCharArray()) {
if (character >= 'a' && character <= 'z') {
character = (char) (character + shift);
if (character > 'z') {
character = (char) (character + 'a' - 'z' - 1);
}
output.append(character);
} else if (character >= 'A' && character <= 'Z') {
character = (char) (character + shift);
if (character > 'Z') {
character = (char) (character + 'A' - 'Z' - 1);
}
output.append(character);
} else if (character >= '0' && character <= '9') {
character = (char) (character + shift);
if (character > '9') {
character = (char) (character + '0' - '9' - 1);
}
output.append(character);
} else {
output.append(character);
}
}
return output.toString();
}
My issue is that I am somehow outputting AAA instead of 999 as far as I can tell from tracing my algo seems solid. I looked through JAVA docs StringBuilder page to see if there was any issue with how I was using it. As far as I can tell it should be good to go.
Could anyone lend me an idea of why my output is the way it is?
Here is the test cases code:
String input_1 = "All-convoYs-9-be:Alert1.";
int rotationFactor_1 = 4;
String expected_1 = "Epp-gsrzsCw-3-fi:Epivx5.";
String output_1 = rotationalCipher(input_1, rotationFactor_1);
check(expected_1, output_1);
String input_2 = "abcdZXYzxy-999.#";
int rotationFactor_2 = 200;
String expected_2 = "stuvRPQrpq-999.#";
String output_2 = rotationalCipher(input_2, rotationFactor_2);
check(expected_2, output_2);
Check your maths
200 % 26 = 18 (shift)
'9' + 18 = 57 + 18 = 75 ('K')
75 + '0' = 75 + 48 = 123 ('{')
123 - '9' = 123 - 57 = 66 ('B')
66 - 1 = 65 ('A')
Now, the problem is, between '9' and 'A' there are 7 other characters, so character = (char) (character + ('0' - '9') - 1); would have to become character = (char) (character + ('0' - '9') - 9); to shift 9 back to 9, but that would screw up you other test case
I don't think ASCII manipulation is the way to go here, as there are characters in-between the digits and the upper and lower cased characters which are going to mess things up as the rotation increases.
In fact, for the digits, you really want to rotate using a factor % 10 instead.
A different approach would be to generate a list of characters and apply a shift to those instead. Now if I was doing this, I'd use List and Collections, but lets assume you can't do that for second, instead, we're going to need to apply a shift to an array, for example...
public String[] rotate(String[] original, int offset) {
if (offset >= 0) {
return positiveRotate(original, offset);
}
return negativeRotate(original, Math.abs(offset));
}
public String[] positiveRotate(String[] original, int offset) {
String[] results = new String[original.length];
int count = original.length - offset;
System.arraycopy(original, count, results, 0, offset);
System.arraycopy(original, 0, results, offset, count);
return results;
}
public String[] negativeRotate(String[] original, int offset) {
String[] results = new String[original.length];
System.arraycopy(original, offset, results, 0, original.length - offset);
System.arraycopy(original, 0, results, original.length - offset, offset);
return results;
}
Now, this has two different methods, one of a "positive" (or "right" shift) and one for a "negative" (or "left" shift). During testing, I found that the you want to "left shift" the array.
Next, we need what we want to shift over...
private String[] digits = "0123456789".split("");
private String[] characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
I've cheated here, you may need to create the array by long hand, but I can't be bothered typing it out.
Please note
I'm using a String array instead of a char array, not hard to change, but I'm been lazy
You could actually do this on the String directly, using things like contains and split to perform the shifting
And then the rotational cipher might look something like...
public String rotationalCipher(String input, int rotationFactor) {
int shift = rotationFactor % 26;
String[] shiftedDigits = rotate(digits, -(rotationFactor % 10));
String[] shiftCharacters = rotate(characters, -(rotationFactor % 26));
StringBuilder output = new StringBuilder();
for (char character : input.toCharArray()) {
String value = Character.toString(character);
int index = 0;
if ((index = indexOf(value, digits)) > -1) {
output.append(shiftedDigits[index]);
} else if ((index = indexOf(value, characters)) > -1) {
output.append(shiftCharacters[index]);
} else if ((index = indexOf(value.toUpperCase(), characters)) > -1) {
output.append(shiftCharacters[index].toLowerCase());
} else {
output.append(value);
}
}
return output.toString();
}
protected int indexOf(String value, String[] array) {
for (int index = 0; index < array.length; index++) {
if (array[index].equals(value)) {
return index;
}
}
return -1;
}
Then you could just execute it something like...
System.out.println(" --> All-convoYs-9-be:Alert1.");
System.out.println(" Got " + rotationalCipher("All-convoYs-9-be:Alert1.", 4));
System.out.println("Want Epp-gsrzsCw-3-fi:Epivx5.");
System.out.println("");
System.out.println(" --> abcdZXYzxy-999.#");
System.out.println(" Got " + rotationalCipher("abcdZXYzxy-999.#", 200));
System.out.println("Want stuvRPQrpq-999.#");
Which outputs
--> All-convoYs-9-be:Alert1.
Got Epp-gsrzsCw-3-fi:Epivx5.
Want Epp-gsrzsCw-3-fi:Epivx5.
--> abcdZXYzxy-999.#
Got stuvRPQrpq-999.#
Want stuvRPQrpq-999.#
As I said above, you could just use a String instead of an array, in which case it might look something more like...
private String digits = "0123456789";
private String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public String rotationalCipher(String input, int rotationFactor) {
int shift = rotationFactor % 26;
String shiftedDigits = rotate(digits, -(rotationFactor % 10));
String shiftCharacters = rotate(characters, -(rotationFactor % 26));
StringBuilder output = new StringBuilder();
for (char character : input.toCharArray()) {
String value = Character.toString(character);
int index = 0;
if ((index = digits.indexOf(value)) > -1) {
output.append(shiftedDigits.charAt(index));
} else if ((index = characters.indexOf(value)) > -1) {
output.append(shiftCharacters.charAt(index));
} else if ((index = characters.indexOf(value.toUpperCase())) > -1) {
output.append(Character.toLowerCase(shiftCharacters.charAt(index)));
} else {
output.append(value);
}
}
return output.toString();
}
public String rotate(String original, int offset) {
if (offset >= 0) {
return positiveRotate(original, offset);
}
return negativeRotate(original, Math.abs(offset));
}
public String positiveRotate(String original, int offset) {
String prefix = original.substring(original.length() - offset);
String sufix = original.substring(0, original.length() - offset);
return prefix + sufix;
}
public String negativeRotate(String original, int offset) {
String prefix = original.substring(offset);
String sufix = original.substring(0, offset);
return prefix + sufix;
}

restore original word after Caesar Cipher shifting

I write a func of Caesar Cipher.
So after I shift a sentence, I want also shift back to the original sentence.
For now it works only for one direction, when I shift with natural positive number, but when I try to do this with negative number, it goes on to value less than 97 of ascii lowercase letters.
I give an example:
word: java
key = 10
output: tkfk
Now I want to shift back, to restore my word from tkfk to java.
key = -10
output: ja\a
Instead of v it put \
I know its happens couse from f to minus 10 from ascii table is the letter '\' and I want the letter v.
I think I need to manipulate this line, but I dont know how, I'm a little bit stuck and I don't have an idea what to do.
char ch = (char) (((int) text[index].charAt(i) + key-97) % 26+97)
My method: (little bit long)
public static void MakeCipherText(String[] text, int key) {
int index =0;
if (key > 0) {
if( text[index] == null || text[index].equals("")) {
System.out.println("No sentences to fix capital letters.");
} else {
while(text[index] != null && !text[index].equals("")) { // only if we have sentence in array or array not contain empty sentence we go through loop
String chiPstr = "";
for(int i=0; i<text[index].length(); i++) {//we work in every itration on 1 sentence (1 index of str array)
if(Character.isLowerCase(text[index].charAt(i))) {//if we have lower letter than:
char ch = (char) (((int) text[index].charAt(i) + key-97) % 26+97); //we put asci value + Cipher value
chiPstr = chiPstr + ch; //each time we add to the new sentece the result
} else if(Character.isUpperCase(text[index].charAt(i))) {//same thing like here, but its work on uppercase letters.
char ch = (char) (((int) text[index].charAt(i) + key-65) % 26+65);
chiPstr = chiPstr + ch;
}else {// if we have space, or other characters that is no a letter, we just put him as is in a sentence.
chiPstr = chiPstr + text[index].charAt(i);
}
}
text[index] = chiPstr;
index ++;
}
}
} else { // key is negetive number
if( text[index] == null || text[index].equals("")) {
System.out.println("No sentences to fix capital letters.");
} else {
while(text[index] != null && !text[index].equals("")) { // only if we have sentence in array or array not contain empty sentence we go through loop
String chiPstr = "";
for(int i=0; i<text[index].length(); i++) {//we work in evry itration on 1 sentence (1 index of str array)
if(Character.isLowerCase(text[index].charAt(i))) {//if we have lower letter than:
char ch = (char) (((int) text[index].charAt(i) + key-97) % 26+97); //we put asci value + Cipher value
chiPstr = chiPstr + ch; //each time we add to the new sentece the result
} else if(Character.isUpperCase(text[index].charAt(i))) {//same thing like here, but its work on uppercase letters.
char ch = (char) (((int) text[index].charAt(i) + key-65) % 26+65);
chiPstr = chiPstr + ch;
}else {// if we have space, or other characters that is no a letter, we just put him as is in a sentence.
chiPstr = chiPstr + text[index].charAt(i);
}
}
text[index] = chiPstr;
index ++;
}
}
}
}
Any suggestion?
As the comments suggest you should really check your code again this will also help you to be a better programmer. But anyway you think too complicated.
If you check your else part that is the exact copy of the if part. And that is no wonder. To decode Caesar cipher you encode it basically again with the right key to encode.
For example:
If you encode it with A => B or in this example with 1:
test--> uftu
so how can we decode uftu back?
When we shift it with B=>A or in this case with 25.
uftu --> test
So in your requirement you want if you put -1 that you decode text that was encoded with 1 before.
So basically we have to find a method to map -1 to 25, -2 to 24 and so on.
And the key function is: modulo
-2 % 26 => 24
-1 % 26 => 25
...
In addition you can even now put numbers bigger than 26 because:
500 % 26 => 6
-500 % 26 => 20
and because 2 % 26 => 2 you don't even need that if clause. Your code looks like this in the end:
public static void MakeCipherText(String[] text, int key) {
int index =0;
key = (((key % 26) + 26) % 26); // See below for explanation of this weird modulo
if( text[index] == null || text[index].equals("")) {
System.out.println("No sentences to fix capital letters.");
} else {
while(text[index] != null && !text[index].equals("")) { // only if we have sentence in array or array not contain empty sentence we go through loop
String chiPstr = "";
for(int i=0; i<text[index].length(); i++) {//we work in every itration on 1 sentence (1 index of str array)
if(Character.isLowerCase(text[index].charAt(i))) {//if we have lower letter than:
char ch = (char) (((int) text[index].charAt(i) + key-97) % 26+97); //we put asci value + Cipher value
chiPstr = chiPstr + ch; //each time we add to the new sentece the result
} else if(Character.isUpperCase(text[index].charAt(i))) {//same thing like here, but its work on uppercase letters.
char ch = (char) (((int) text[index].charAt(i) + key-65) % 26+65);
chiPstr = chiPstr + ch;
}else {// if we have space, or other characters that is no a letter, we just put him as is in a sentence.
chiPstr = chiPstr + text[index].charAt(i);
}
}
text[index] = chiPstr;
index ++;
}
}
}
Never forget to use functions and don't use duplicate code. Bad style and error prone. The solution is quite easy if you think it through.
Information weird modulo function
You see I use a weird modulo function. Because in Java % don't calculate the modulo but the remainder. (Different then in Python).
So to get the "true" modulo in Java we have to use this weird trick:
Reference: What's the difference between “mod” and “remainder”?
key = (((key % 26) + 26) % 26);

Converting java string to uppercase without using toUpperCase method

What I have so far
public String toUpperCase(String str)
{
int strLength = str.length();
String word = "";
while (strLength > 0)
{
char newStr = str.charAt(strLength);
Character.toUpperCase(newStr);
Character.toString(newStr);
word += newStr;
strLength--;
}
return word;
}
I'm completely lost and my code is probably illogical. Some help would be appreciated
To change string into uppercase without using toUpperCase(), then check below code.
if(ch>96 && ch<123)
{
ch=ch-32;
System.out.print( (char) ch);
}
check char small letter or not , if small then subtract it from 32
The Character.toUpperCase() function returns a character. You have to hold that value in a char literal.
Plus, you should begin the loop at strLength - 1 otherwise it'll throw a StringIndexOutOfBounds error. And, you should iterate till the index 0. At present, you iterate from strLength till 1.
Lastly, since you are iterating in the opposite direction, you should take care of the way you append the characters.
public String toUpperCase(String str)
{
int strLength = str.length();
String word = "";
while (--strLength >= 0)
{
char newChar = str.charAt(strLength);
newChar = Character.toUpperCase(newChar);
word = newChar + word; //appending in reverse order
}
return word;
}
NOTE
Since you are asked to write the function considering that the function doesn't exist, it would be less ironical to NOT use the Character.toUpperCase() method. Even I have used it. In that case, you can take a look at #Sam's suggestion. The above code will then have this modification:
char newChar = str.charAt(strLength);
if((int)newChar > 96 && (int)newChar < 123)
newChar = newChar - 32;
//the if statement is the replacement of the Character.toUpperCase()
//function call
You should take a look at the ASCII table.
char are integer, 'A' to 'Z' is 65 to 90 and 'a' to 'z' is 97 to 122.
So if you have a char between 'a' and 'z' you want to substract the difference betwin 'A' and 'a' to your char.
'a' - ('a' - 'A') = 'A'
'c' - ('a' - 'A') = 'C'
So you need to iterate on your string and do your math on each character.
public static char myUpper(char c){
if (c >= 'a' || c <= 'z'){
c = (char) (c - ('a' - 'A'));
}
return c;
}
Personally this is what I would do.
String newsentence =""; String question="HeLlo";
String caps = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
String lowerCase = "abcdefghijklmnopqrstuvwxyz";
for(int i=0;i<caps.length();i++) {
for(int j=0;j<caps.length();j++) {
if(question.equals(lowerCase.subString(j,j+1)){ newsentence+= (caps.subString(i,i+1); }
}} System.out.print(newsentence);
This is what I would do.

caesar shift cipher java

I'm trying to implement a basic Caesar Shift Cipher for Java to shift all the letters by 13. Here's my code so far.
public static String cipher(String sentence){
String s = "";
for(int i = 0; i < sentence.length(); i++){
char c = (char)(sentence.charAt(i) + 13);
if (c > 'z')
s += (char)(sentence.charAt(i) - 13);
else
s += (char)(sentence.charAt(i) + 13);
}
return s;
}
However, the program also changes the values of numbers and special characters and I don't want that.
String sentence = "abc123";
returns "nop>?#"
Is there a simple way to avoid the special characters and only focus on letters?
Edit: I should mention I want to keep all the other bits. So "abc123" would return "nop123".
In the following example I encrypt just the letters (more precisely A-Z and a-z) and added the possibility to use any offset:
public static String cipher(String sentence, int offset) {
String s = "";
for(int i = 0; i < sentence.length(); i++) {
char c = (char)(sentence.charAt(i));
if (c >= 'A' && c <= 'Z') {
s += (char)((c - 'A' + offset) % 26 + 'A');
} else if (c >= 'a' && c <= 'z') {
s += (char)((c - 'a' + offset) % 26 + 'a');
} else {
s += c;
}
}
return s;
}
Here some examples:
cipher("abcABCxyzXYZ123", 1) // output: "bcdBCDyzaYZA123"
cipher("abcABCxyzXYZ123", 2) // output: "cdeCDEzabZAB123"
cipher("abcABCxyzXYZ123", 13) // output: "nopNOPklmKLM123"
Note: Due to your code, I assumed that you just want to handle/encrypt the "ordinary" 26 letters. Which means letters like e.g. the german 'ü' (Character.isLetter('ü') will return true) remain unencrypted.
Problem is that you add 13 as a fixed number, and that will for some letters (in second half of alphabet mostly and digits) produce characters that aren't letters.
You could solve this by using array of letters and shifting through those characters. (similar for digits) So something like this
List<Character> chars = ... // list all characters, separate lists for upper/lower case
char c = chars.get((chars.indexOf(sentence.charAt(i)) + 13)%chars.size());

Counting Duplicates and Grouping in an Arraylist

for my project we have to manipulate certain LISP phrasing using Java. One of the tasks is given:
'(A A A A B C C A A D E E E E)
Group the duplicates and make the output like:
′((4A)(1B)(2C)(2A)(1D)(4E))
Notice how the first four A's are kept separate from the last 2...
My issues is with keeping track of how many is each letter. I added the given letters into an array list and I manipulated it a little:
for (int i=0;i<list.size();i++)
{
String val=list.get(i);
String first=list.get(0);
while (val.equals(first))
{
total+=1;
val="X";
}
}
Total should be the number of times of the first occurrence but it keeps giving me 6. 6 is the correct number for all the A's in the sequence, but how do I get it to stop at the first four, record the number and move on the the next letter?
Here is with Java 8 Stream API.
Map<Character, Long> countedDup = Arrays.asList('A' ,'A' ,'A', 'A', 'B', 'C', 'C', 'A', 'A', 'D', 'E', 'E', 'E', 'E')
.stream().collect(Collectors.groupingBy(c -> c, Collectors.counting()));
System.out.println(countedDup);//{A=6, B=1, C=2, D=1, E=4}
This is my solution:
for (int i = 0; i < list.size(); i++) {
String first = list.get(i);
int total = 1;
while (i + 1 < list.size() && first.equals(list.get(i + 1))) {
total++;
i++;
}
System.out.println("(" + total + first + ")");
}
You can debug to know how it run.
***** Note that when compare 2 String objects, never use ==. Use method equals()
This is my solution, though didn't used arrayList:
Edited: as per pointed out by Ascalonian
String text = "'(A A V A B C C X X X X A A Z Z Z K L N N N N N)";
int counter = 1;
System.out.print("'(");
for (int i = 2; i < text.length() - 1; i = i + 2) {
System.out.print("(");
while (((i + 2) < text.length())
&& (text.charAt(i) == text.charAt(i + 2))) {
++counter;
i += 2;
}
System.out.print(counter + "" + text.charAt(i) + ")");
counter = 1;
}
System.out.print(")");
o/p will be for sample text:
((2A)(1V)(1A)(1B)(2C)(4X)(2A)(3Z)(1K)(1L)(5N))
Not sure if using an ArrayList is a requirement, but if not, I use a different approach where I get just the letters, strip away the white spaces, make it a char[] and iterate each letter.
// the initial LISP phrase
String lispPhrase = "'(A A A A B C C A A D E E E E)";
// Pull out just the letters
String letters = lispPhrase.substring(2, lispPhrase.length()-1);
// Remove the white space between them
letters = letters.replaceAll("\\s","");
// Get an array for each letter
char[] letterArray = letters.toCharArray();
char previousLetter = ' ';
char nextLetter = ' ';
int letterCounter = 1;
System.out.print("'(");
// now go through each letter
for (char currentLetter : letterArray) {
// If the first time through, set previousLetter
if (previousLetter == ' ') {
previousLetter = currentLetter;
}else {
nextLetter = currentLetter;
// Is the next letter the same as before?
if (previousLetter == nextLetter) {
letterCounter++;
}else {
// If the next letter is different, print out the previous letter and count
System.out.print("(" + letterCounter + previousLetter + ")");
// Reset the counter back to 1
letterCounter = 1;
}
previousLetter = nextLetter;
}
}
System.out.print("(" + letterCounter + previousLetter + "))");
This gives the output of:
'((4A)(1B)(2C)(2A)(1D)(4E))
In order to get the required output that you specified in your question, I would keep track of the current and next values in the given list. The element count starts at 1 and is incremented when the equality condition is true, otherwise it is reset to 1. And the final output is only updated when the equality condition is false.
Sample code:
EDIT I updated my code based on #Ascalonian's suggestion.
String inputString = "'(A A A A B C C A A D E E E E)";
// I assume that the input always starts with "'("
String finalOutput = inputString.substring(0, 2);
int valCount = 1;
String valCurrent = inputString.substring(2, 3);
String valNext = "";
for (int i = 4; i < inputString.length(); i++) {
valNext = inputString.substring(i, i + 1);
if (valNext.equals(" ")) {
continue;
}
if (valCurrent.equals(valNext)) {
valCount++;
} else {
finalOutput += "(" + valCount + valCurrent + ")";
valCount = 1;
}
valCurrent = valNext;
}
finalOutput += ")";
System.out.println(finalOutput);
Output string: '((4A)(1B)(2C)(2A)(1D)(4E))
I hope this helps.

Categories