The code below encrypts a word or sentence in the way Caesar did. You put the shift value and the program takes each letter of the word/sentence and "moves" in the alphabet acoording to the shift (key) value. But this is not the problem.
I found the code on the internet and i cannot explain some of it's lines.
I know how it works but i need some specific answer about some of it's lines.
Here is the code:
import acm.program.*;
public class CaesarCipher extends ConsoleProgram {
public void run() {
println("This program implements a Caesar cipher.");
int key = readInt("Character positions to shift: ");
String plaintext = readLine("Enter a message: ");
String ciphertext = encodeCaesarCipher(plaintext, key);
println("Encoded message: " + ciphertext);
}
private String encodeCaesarCipher(String str, int key) {
if (key < 0) key = 26 - (-key % 26);
String result = "";
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if (Character.isLetter(ch)) {
if (Character.isUpperCase(ch)) {
ch = (char) ('A' + (ch - 'A' + key) % 26);
}
else {
ch = (char) ('a' + (ch - 'a' + key) % 26);
}
}
result += ch;
}
return result;
}
}
What exactly do those lines mean, and how do they do what they do?
ch = (char) ('A' + (ch - 'A' + key) % 26);
and
ch = (char) ('a' + (ch - 'a' + key) % 26);
Those two lines behave identically, except for the fact that one applies to uppercase, and one to lowercase. I'll explain the uppercase operations here. Keep in mind that these char values are treated as ints until step 6 below.
ch = (char) ('A' + (ch - 'A' + key) % 26);
ch - 'A' gives the initial character's int offset from capital-A.
ch - 'A' + key increases the offset by key, creating the ciphered character's offset from capital-A.
(ch - 'A' + key) % 26: the modulo here ensures that the numeric value of the ciphered character is 0-25 (for the 26 letters in the alphabet), preventing alphabet "overflow." The value of that expression is now the normalized offset of the ciphered character from capital-A.
'A' + (ch - 'A' + key) % 26 adds the ciphered character's offset from capital-A to capital-A itself, resulting in the ciphered character's int value.
(char) ('A' + (ch - 'A' + key) % 26) casts that int to char type, resulting in the ciphered character as a char.
This code treats the beginning of the alphabet (capital-A) as the "starting point," from which each letter's offset is measured. The offset is the character's number of letters from 'A'.
Example: To cipher "E" with a key of 6:
Find the number of letters between "E" and capital-A. The ASCII code of A is 65, and the code of E is 69. To find E's offset from A, subtract 69 - 65 = 4.
Add the offset and the key: 4 + 6 = 10. This is the number of letters from A the ciphered character will be.
10 % 26 = 10 (no effect because of the small starting letter + small key)
'A' + 10 = 65 + 10 = 75 results in the ASCII code of the ciphered character, 75 ('K')
Casting 75 to char allows it to be treated as a character, rather than an int.
It is converting the character to the ASCII equivalent, as denoted by the + operator (you cannot add characters, only numbers), then changing the character by a certain number of values. The (char) is casting (converting) the result back to a character so you can assign it to ch.
'A's ASCII value is 65, and ch is the character currently being evaluated at position i in the string. ch = (char) ('A' + (ch - 'A' + key) % 26); evaluates as '65 + (character's ASCII number - 65 + key) % 26'. This result is then cast back to a character and assigned to ch. The modulo (%) operator takes a number, divides it by a second number, in this case 26, and returns the remainder (5 % 6 is 5, while 7 % 3 is 1). The second statement works the same way, but uses lowercase 'a' (97) instead of capital 'A'.
See this page for ASCII keys: http://www.asciitable.com/
Related
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);
Hey so recently I got tasked with creating an app that reads a message and ecrypts it with the Caesar cipher in Java.
I didn't really have a problem until I came to the part where adding the numberic cipher would take you over letters a-z/A-Z into special symbols and I did not really know what to do.
Here is the code of my solution:
private String caesarCipher(String message) {
Scanner input = new Scanner(System.in);
StringBuilder cipher = new StringBuilder();
char ch;
int key;
System.out.print("Enter a key: ");
key = Integer.parseInt(input.nextLine());
for(int i = 0; i < message.length(); i++) {
ch = message.charAt(i);
if(ch >= 'a' && ch <= 'z'){
ch = (char)(ch + key);
if(ch > 'z'){
ch = (char)(ch - 'z' + 'a' - 1);
}
cipher.append(ch);
}
else if(ch >= 'A' && ch <= 'Z'){
ch = (char)(ch + key);
if(ch > 'Z'){
ch = (char)(ch - 'Z' + 'A' - 1);
}
cipher.append(ch);
}
else {
cipher.append(ch);
}
}
return cipher.toString();
}
Could someone please explain to me the process and reasoning behind the following statement:
if(ch > 'z'){
ch = (char)(ch - 'z' + 'a' - 1);
}
It will never allow an encrypted character to exceed its supposed range, that is, a - z. The ascii of a and z are 97 and 122 respectively, and you would want the Caesar cipher encryption within this range of characters only.
This will check if the ascii code of ch is greater than the ascii of z
if(ch > 'z'){
If yes, it will calculate: (ascii of ch) - (ascii of z) + (ascii of a) - 1.
ch = (char)(ch - 'z' + 'a' - 1);
It is translated into ch = (char)(ch - 122 + 97 - 1);
Suppose you want to encrypt character a with a key 3. The program will take 97 (ascii of a) + 3. You will get 100, which is the ascii of d. Yet, what if you want to encrypt z with a key 3?
Same as before, it will take 122 (ascii of z) + 3 = 125. However, 125 is not found in the range 97 - 122 (a-z). Thus, you will get an unwanted character (in this case, 125 is the ascii of }).
Therefore, (ch - 'z' + 'a' - 1) will ensure that any character exceeding ascii 122, will be converted back into a character with ascii range of 97 and 122 inclusive only. In the example of 125, ch = (char)(125 - 122 + 97 - 1) => ch = (char)(99) => ch = c.
I have trouble understanding this simple method for encrypting characters in a string. So here's the method:
encryptedChar = (char) (’A’ + (originalChar -’A’ + offset) % 26);
I don't understand the need for 'A' - 'A' since they just cancel out. What is the reason behind it?
Why shouldn't I just use the following method?
encryptedChar = (char) ((originalChar + offset) % 26);
Shouldn't it work exactly the same?
encryptedChar = (char) ('A' + (originalChar -'A' + offset) % 26);
The two 'A' don't cancel each other, since the second one is inside the expression that is the operand of the modulus operator.
'A' + (originalChar -'A' + offset) % 26 - here each letter is mapped to a different letter.
((originalChar + offset) % 26) - here each letter is mapped to a character whose int value is between 0 and 25.
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());
Here is a short program that counts the letters of any given word entered by the user.
I'm trying to figure out what the following lines actually do in this program:
counts[s.charAt(i) - 'a']++; // I don't understand what the - 'a' is doing
System.out.println((char)('a' + i) // I don't get what the 'a' + i actually does.
import java.util.Scanner;
public class Listing9_3 {
public static void main(String[] args) {
//Create a scanner
Scanner input = new Scanner (System.in);
System.out.println("Enter a word to find out the occurences of each letter: ");
String s = input.nextLine();
//Invoke the count Letters Method to count each letter
int[] counts = countLetters(s.toLowerCase());
//Display results
for(int i = 0; i< counts.length; i++){
if(counts[i] != 0)
System.out.println((char)('a' + i) + " appears " +
counts[i] + ((counts[i] == 1)? " time" : " times"));
***//I don't understand what the 'a' + i is doing
}
}
public static int[] countLetters(String s) {
int[] counts = new int [26]; // 26 letters in the alphabet
for(int i = 0; i < s.length(); i++){
if(Character.isLetter(s.charAt(i)))
counts[s.charAt(i) - 'a']++;
***// I don't understand what the - 'a' is doin
}
return counts;
}
}
Characters are a kind of integer in Java; the integer is a number associated with the character on the Unicode chart. Thus, 'a' is actually the integer 97; 'b' is 98, and so on in sequence up through 'z'. So s.charAt(i) returns a character; assuming that it is a lower-case letter in the English alphabet, subtracting 'a' from it gives the result 0 for 'a', 1 for 'b', 2 for 'c', and so on.
You can see the first 4096 characters of the Unicode chart at http://en.wikibooks.org/wiki/Unicode/Character_reference/0000-0FFF (and there will be references to other pages of the chart as well). You'll see 'a' there as U+0061 (which is hex, = 97 decimal).
Because you want your array to contains only the count of each letter from 'a' to 'z'.
So to index correctly each count of the letter within the array you would need a mapping letter -> index with 'a' -> 0, 'b' -> 1 to 'z' -> 25.
Each character is represented by a integer value on 16 bits (so from 0 to 65,535). You're only interested from the letters 'a' to 'z', which have respectively the values 97 and 122.
How would you get the mapping?
This can be done using the trick s.charAt(i) - 'a'.
This will ensure that the value returned by this operation is between 0 and 25 because you know that s.charAt(i) will return a character between 'a' and 'z' (you're converting the input of the user in lower case and using Character.isLetter)
Hence you got the desired mapping to count the occurences of each letter in the word.
On the other hand, (char)('a' + i) does the reverse operation. i varies from 0 to 25 and you respectively got the letters from 'a' to 'z'. You just need to cast the result of the addition to char otherwise you would see its unicode value be printed.
counts[s.charAt(i) - 'a']++; // I don't understand what the - 'a' is doing
assume charAT(i) is 'z'
now z-a will be equal to 25 (subtract the unicode / ASCII values).
so counts[25]=counts[25]+1; // just keeps track of count of each character