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());
Related
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
Essentially, I am confused on the purpose of this code and what it does ( cAlphabet = (char)(cAlphabet - 'a' + 'z' + 1); ), in this encryption code, could someone please explain how this works thanks!
System.out.println(" Input the ciphertext message : ");
String ciphertext = sc.nextLine();
System.out.println(" Enter the shift value : ");
int shift = sc.nextInt();
String decryptMessage = "";
for (int i = 0; i < ciphertext.length(); i++)
{
// Shift one character at a time
char alphabet = ciphertext.charAt(i);
// if alphabet lies between a and z
if (alphabet >= 'a' && alphabet <= 'z')
{
// shift alphabet
alphabet = (char)(alphabet + shift);
// shift alphabet less than 'a'
if (cAlphabet < 'a')
{
//reshift to starting position
cAlphabet = (char)(cAlphabet - 'a' +'z' + 1);
}
cAlphabet = (char)(cAlphabet - 'a' + 'z' + 1);
When you make some operations(sum, minus, etc) with Char it wiil use numeric code of character. So you will get new numeric code and convert it back to the character.
this is almost the same as cAlphabet = (char)(cAlphabet - 219 + 97 + 1);
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);
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.
I have to handle some strings, I should put them N positions to left to organize the string.
Here's my code for while:
private String toLeft() {
String word = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // Example
byte lpad = 2; // Example
StringBuilder sb = new StringBuilder();
for (int i = 0; i < word.length(); i++) {
sb.append((char) (word.charAt(i) - lpad));
}
return sb.toString();
}
It's working for inputs that don't have to move many times...
So, the problem is that when the number N of positions to move is a little bit large (like 10), it returns me non letters, like in the example below, what can I do to prevent it?
Ex.: ABCDEFGHIJKLMNOPQRSTUVWXYZ if I move each char 10 positions to left it returns 789:;<=>?#ABCDEFGHIJKLMNOP while it must return QRSTUVWXYZABCDEFGHIJKLMNOP.
Some inputs and their expected outputs:
VQREQFGT // 2 positions to left == TOPCODER
ABCDEFGHIJKLMNOPQRSTUVWXYZ // 10 positions to left == QRSTUVWXYZABCDEFGHIJKLMNOP
LIPPSASVPH // 4 positions to left == HELLOWORLD
I think you have misunderstood what your (homework?) requirements are asking you to do. Lets look at your examples:
VQREQFGT // 2 positions to left == TOPCODER
Makes sense. Each character in the output is two characters before the corresponding input. But read on ...
ABCDEFGHIJKLMNOPQRSTUVWXYZ // 10 positions to left == QRSTUVWXYZABCDEFGHIJKLMNOP
Makes no sense (literally). The letter Q is not 10 characters before A in the alphabet. There is no letter in the alphabet that is before A in the alphabet.
OK so how do you get from A to Q in 10 steps?
Answer ... you wrap around!
A, Z, Y, X, W, V, U, T, S, R, Q ... 10 steps: count them.
So what the requirement is actually asking for is N characters to the left with wrap around. Even if they don't state this clearly, it is the only way that the examples "work".
But you just implemented N characters to the left without wrap around. You need to implement the wrap around. (I won't show you how, but there lots of ways to do it.)
There's another thing. The title of the question says "Decrement only letters" ... which implies to me that your requirement is saying that characters that are not letters should not be decremented. However, in your code you are decrementing every character in the input, whether or not it is a letter. (The fix is simple ... and you should work it out for yourself.)
what can I do to prevent it?
You make it wrap around.
If you want a value to go from 10 to 19 and then start at 10 again, in each iteration you subtract 10, increase by one, take the remainder of that divided by 20, and add 10 again.
Only here, 10 is 'A', 19 is Z, and instead of increasing by one, we add or subtract n.
private String toLeft() {
String word = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // Example
byte lpad = 10; // Example
StringBuilder sb = new StringBuilder();
int n = -lpad; // I'm using n here to I can use '+ n' below
for (int i = 0; i < word.length(); i++) {
int shifted = word.charAt(i) - 'A' + n;
shifted %= ('Z' - 'A' + 1); // This is for positive n
while(shifted < 0) // And this for negative ones
{
shifted += ('Z' - 'A' + 1);
}
sb.append((char)(shifted + 'A'));
}
return sb.toString();
}
Please read #StephenC's excellent answer about wrap-around. In short, you don't shift left, you rotate left, such that B → A → Z → Y. When you rotate, you wrap around to the other end.
So, for letters you want A-Z to rotate. The easiest rotation method is using modulus (%).
Your logic will be as follows:
Convert letters A-Z into numbers 0-25: n = ch - 'A'
Apply shift and wrap around. Since you're shifting left, you're subtracting from the number, so to prevent negative numbers, you start by shifting a full cycle to the right: n = (n + 26 - shift) % 26
Convert numbers back to letters: ch = (char)(n + 'A')
Here is the code:
private static String rotateLeft(String text, int count) {
char[] buf = text.toCharArray();
for (int i = 0; i < buf.length; i++)
buf[i] = (char)((buf[i] - 'A' + 26 - count) % 26 + 'A');
return new String(buf);
}
Of course, you should validate input, and test your code:
private static String rotateLeft(String text, int count) {
char[] buf = text.toCharArray();
if (count <= 0 || count >= 26)
throw new IllegalArgumentException("Invalid count: " + count);
for (char ch : buf)
if (ch < 'A' || ch > 'Z')
throw new IllegalArgumentException("Invalid character: " + ch);
for (int i = 0; i < buf.length; i++)
buf[i] = (char)((buf[i] - 'A' + 26 - count) % 26 + 'A');
return new String(buf);
}
public static void main(String[] args) {
System.out.println(rotateLeft("VQREQFGT", 2));
System.out.println(rotateLeft("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 10));
System.out.println(rotateLeft("LIPPSASVPH", 4));
}
OUTPUT
TOPCODER
QRSTUVWXYZABCDEFGHIJKLMNOP
HELLOWORLD