I have to Implement a static public method named encodeCaesar in the class Functionality.java, which encodes a text using Caesar encryption and I am a complete novice in Java.
Signature: encodeCaesar(String s, int val) : String
The method gets a string value and an integer value as input parameters. The letters (characters) from the string value are to be shifted by the integer value. For simplicity, I can assume that there are only letters and no spaces, numbers or special characters.
The string value should be converted to lower case before the encryption is performed. The method should return a string where each letter has been moved according to the specified integer value.
Example: encodeCaesar("Ac",3) returns "df". If the given integer value is less than 0 or greater than 26, an empty string should be returned.
public class Functionality {
public static void main(String[] args) {
}
public static String encodeCaesar(String s, int val) {
char[] newString = s.toCharArray();
for (int i = 0; i < s.length(); i++) {
int newChar = newString[i] + val + 26;
// Handle uppercase letters
while (Character.isUpperCase(newString[i]) && newChar >= 65 + 26) {
newChar -= 26;
}
// Handle lowecase letters
while (Character.isLowerCase(newString[i]) && newChar >= 97 + 26) {
newChar -= 26;
}
newString[i] = (char) (newChar);
}
return String.valueOf(newString);
}
}
My problem is that in return it give me only true or false. How can I solve this: The method should return a string where each character has been moved according to the specified integer value.
The following code snippet addresses some minor issues:
non-letter characters (if any) remain unchanged by the cipher
while loop replaced with if
use modulo operator to wrap values over z
public static String encodeCaesar(String s, int val) {
char[] newString = s.toLowerCase().toCharArray();
for (int i = 0; i < newString.length; i++) {
char newChar = newString[i];
if (newChar >= 'a' && newChar <= 'z') {
newString[i] = (char) ('a' + (newChar + val - 'a') % 26);
}
}
return new String(newString); // String.valueOf calls it anyway
}
Comparison:
System.out.println("encode: " + encodeCaesar("Viva Caesar! Morituri te salutant!", 3));
System.out.println("caesar: " + caesar("Viva Caesar! Morituri te salutant!", 3));
Output:
encode: ylyd fdhvdu! prulwxul wh vdoxwdqw!
caesar: ylyd=fdhvdu>=prulwxul=wh=vdoxwdqw>
public static String caesar(String s, int val) {
char[] newString = s.toLowerCase().toCharArray();
for (int i = 0; i < s.length(); i++) {
int newChar = newString[i] + val + 26;
// Handle lowercase letters
while (Character.isLowerCase(newString[i]) && newChar >= 97 + 26) {
newChar -= 26;
}
newString[i] = (char) (newChar);
}
return String.valueOf(newString);
}
I solved the problem.
You can use String.codePoints method to iterate over int values of the characters of this string, and shift them by a given value:
public static String encodeCaesar(String s, int val) {
if (val < 0 || val > 26) return "";
return s.codePoints()
// Stream<Character>
.mapToObj(i -> (char) i)
// convert to lowercase
.map(Character::toLowerCase)
// filter out non-letters
.filter(ch -> ch >= 'a' && ch <= 'z')
// if a letter should be shifted
// outside the alphabetical range,
// then move it before the beginning
.map(ch -> ch + val > 'z' ? ch - 'z' + 'a' - 1 : ch)
// shift letter by a given value
.map(ch -> ch + val)
// each character as a single string
.map(Character::toString)
// join characters into one string
.collect(Collectors.joining());
}
public static void main(String[] args) {
System.out.println(encodeCaesar("Ac", 3)); // df
System.out.println(encodeCaesar("YZx 32*d", 3)); // bcag
}
See also: Replace non ASCII character from string
Related
I have a problem with this exercise.
In this exercise I enter a word and a number. I have to use the ASCII table to encrypt the word.
If I enter the letter "a" and the number 3, the letter becomes "d".
If I enter the letter "z" and the number 2, the letter should become "b", but a symbol comes out.
Another problem is if I use an uppercase letter. If I enter the uppercase letter "Z" I'll still get a symbol.
Another problem is if I use the letters "aB" and the number -2 should come out "yZ", but symbols come out.
This is the exercise:
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
String s, n="";
int N;
System.out.println("Insert a word");
s=in.next();
System.out.println("Insert a number");
N=in.nextInt();
n=uno(s, N);
System.out.println("That's the encrypted word: " + n);
}
public static String uno (String s, int N) {
String f, n="";
int c;
int length = s.length();
for (int i=0; i<length; i++) {
c = s.charAt(i);
c=c+N;
f = Character.toString((char)c);
n=n+f;
}
return n;
}
ASCII for z is 122. You add 2 to that. The ASCII for 124 is | symbol.
You need to check if your addition is going out of range (i.e. above 122).
Note: this won't work is N is greater than 26. Check the solution just below that implements modulo to handle that.
public static String uno (String s, int N) {
String f, n = "";
int c;
int length = s.length();
for (int i = 0; i < length; i++) {
c = s.charAt(i);
c = c + N;
if (c >= 122) {
c -= 26;
}
f = Character.toString((char) c);
n = n + f;
}
return n;
}
Side note: Never concatenate a string in a loop using +. It is very inefficient. Using StringBuilder.
Handle case sensitive letters concisely:
public static String uno (String s, int N) {
StringBuilder n = new StringBuilder();
int bound = s.length();
IntStream.range(0, bound).forEach(i -> {
char c = s.charAt(i);
n.append(Character.isUpperCase(c) ?
(char) ((c + N - 'A') % 26 + 'A') :
(char) ((c + N - 'a') % 26 + 'a'));
});
return n.toString();
}
Handling negative numbers:
public static String uno (String s, int N) {
StringBuilder n = new StringBuilder();
int bound = s.length();
IntStream.range(0, bound).forEach(i -> {
char c = s.charAt(i);
if (N > 0) {
n.append(Character.isUpperCase(c) ?
(char) ((c + N - 'A') % 26 + 'A') :
(char) ((c + N - 'a') % 26 + 'a'));
} else {
n.append((char) (c + N % 26 + 26));
}
});
return n.toString();
}
Check this comment for a good point on your naming conventions.
I need to first encrypt and then decrypt a message using the Vigenere cypher. This is how it should work
example message: "c a t s _ d o g s"
keyword "rats": r a t s r a t s r
order of the letter in the message (start at a=0):2 0 19 18 () 3 14 6 18
order of the letter in the keyword: 17 0 19 18 17 0 19 18 17
sum of the two orders: 19 0 38 36 17 3 39 24 35
new letter for the message* t a m k d h y j
encrypted message = "tamk uoyk"
Note: if the sum > 26 then we subtract 26 from the sum to get a cyclical alphabet. Example:
z + b = 25 + 1 = 26; 26 - 26 = 0 --> a
I have written the methods to obtain the numerical value of the keyword, and also two methods that "add" or "subtract" individual letters and two methods that perform the caesar encoding/decoding (simply shifting the entire message by an int to the right in the alphabet, or to the left to decrypt).
The piece that I really need help for is to how to create a for loop that's going to repeat the keyword the appropriate amount of times (to have the same length as the message) and proceed to the obtainKeys method to get the numerical values of the repeated key.
Here is my entire program; the part I am struggling with is at the end (Q2f)
import java.util.Arrays;
public class Cypher {
public static void main(String[] args) {
System.out.println(charRightShift('z', 3));
System.out.println(charLeftShift('z', 3));
String test = caesarEncode("cats and dogs", 5);
System.out.println(test);
System.out.println(caesarDecode(test, 5));
obtainKeys("abcxyz");
System.out.println(vigenereEncode("elephants", "rats"));
}
//Q2a-b
//Generalized method for char shifts
public static char charShift(char c, int n) {
//value of n should be between 0 and 25
if (Math.abs(n) < 0 || 25 < Math.abs(n)) {
//returning the ascii value of '0' which
//is nul & adding error message
int zero = 0;
c = (char) zero;
throw new IllegalArgumentException("n has to be 0<=|n|<=25");
}
//character c should be a lower case latin letter
//if not, we simply return c, the original character,
//skipping this else if
else if (c >= 'a' && c <= 'z') {
c = (char) (c + n);
if (c > 'z') {
c = (char) (c - 26);
} else if (c < 'a') {
c = (char) (c + 26);
}
}
return c;
}
//method that shifts the value of the character to the right
public static char charRightShift(char c, int n) {
c = charShift(c, n);
return c;
}
//method that shifts the value of the character to the left
public static char charLeftShift(char c, int n) {
n = -n;
c = charShift(c, n);
return c;
}
//Q2c
//method that shifts the message to the right by int 'key' characters
public static String caesarEncode(String message, int key) {
//transform string into char array
char[] messageEncrypt = message.toCharArray();
//for each char, we shift it by 'key' ints,
//using charRightShift method
for (int i = 0; i < messageEncrypt.length; i++) {
char c = messageEncrypt[i];
c = charRightShift(c, key);
messageEncrypt[i] = c;
}
return new String(messageEncrypt);
}
//Q2d
//method that shifts the message to the left by int 'key' characters
public static String caesarDecode(String message, int key) {
//transform string into char array
char[] messageDecrypt = message.toCharArray();
//for each char, we shift it by 'key' ints using charLeftShift
for (int i = 0; i < messageDecrypt.length; i++) {
char c = messageDecrypt[i];
c = charLeftShift(c, key);
messageDecrypt[i] = c;
}
return new String(messageDecrypt);
}
//Q2e
//method to obtain the int array storing the numerical value of the String
public static int[] obtainKeys(String s) {
//creating int array where we're going to
//store the numerical value of the String
int[] keys = new int[s.length()];
int j;
//for each ascii value of the char in string s, we substract 97 to
//get the lower case english alphabet character corresponding to it
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
j = c - 97;
//now store every int in the int array
keys[i] = j;
}
String keysString = Arrays.toString(keys);
return keys;
}
//Q2f
public static String vigenereEncode(String message, String keyword) {
//for loop check if there are any 'illegal' characters in the keyword
char[] kword = keyword.toCharArray();
for (int i = 0; i < kword.length; i++) {
char c = kword[i];
if (c < 'a' || c > 'z') {
throw new IllegalArgumentException(
"The keyword must only contain characters " +
"from the lower case English alphabet.");
}
}
int[] numMessage = obtainKeys(message);
int[] numKeyword = obtainKeys(keyword);
for (int i = 0; i < message.length(); i++) {
for (int j = 0; j < keyword.length(); i++) {
//NOT SURE IF I NEED A NESTED LOOP HERE
//WHAT TO DO HERE?
}
}
return messageVigenere;
}
}
You can do this by using the mod operation %.
char[] messageArray = message.toCharArray();
char[] encryptedMessage = new char[messageArray.length];
int[] numKeyword = obtainKeys(keyword);
int keywordLength = numKeyword.length;
for(int i=0; i<message.length(); i++){
int shiftAmount = numKeyword[i % keywordLength];
char c = messageArray[i];
c = charRightShift(c,shiftAmount);
encryptedMessage[i] = c;
}
Instead of repeating the keyword til it is the length of the message, you can find which letter is needed using modulus logic. For any location n in the message, the keyword letter is keyword[n % keyword.length()].
I am trying to create the addition portion of a vigenere cipher and need to add letters in the alphabet together, resulting in another letter from the alphabet. This must be the standard alphabet with not special characters. All 26 letters.
I can get the number associated with the alphabet number. for example A =0 B=1 ... z=25, So how would i be able to create string full of the letter equivalent of that number?
public String encrypt(String orig, String iv, String key) {
int i, j, result;
String cipherText = "";
int b = iv.length();
//loops through the entire set of chars
for (i = 0; i < text.length; i += b) {
//Splits the char into block the size of the IV block.
for (j = 0; j < b; j++) {
//checks to for first block. If so, begains with iv.
if (i == 0) {
//adding the iv to the block chars
char one = text[j], two = iv.charAt(j);
result = (((iv.charAt(j) - 'a') + (text[j] - 'a')) % 26);
//prints out test result.
System.out.println(one + " + " + (iv.charAt(j) - 'a') + "= " + result);
} else {
//block chainging, addition, with new key.
result = ((key.charAt(j) - 'a') + (text[j + i] - 'a')) % 26;
// System.out.println(result);
}
}
}
return cipherText;
}
I created a new method with a char array and all of the inputs were the alphabet. I called the method with the number in question and returned a char.
public char lookup(int num){
char[] alphabet = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
return alphabet[num];
}
Chars can automatically converted to ints.
For example try this:
final char aChar = 'a';
char bChar = aChar + 1;
char uChar = (char) (bChar + 19);
char qChar = (char) (uChar - 4);
System.out.println(aChar+" "+ bChar + " " + qChar + " " + uChar);
Since the numeric code of 'a' to 'z' are consecutive with the char type (UTF-16), you could simply use addition:
public char lookup(int num) {
return (char)('a' + num);
}
Because char + int will result in an int, we need to cast the type back to char.
I want to write a program that takes a string text, counts the appearances of every letter in English and stores them inside an array.and print the result like this:
java test abaacc
a:***
b:*
c:**
* - As many time the letter appears.
public static void main (String[] args) {
String input = args[0];
char [] letters = input.toCharArray();
System.out.println((char)97);
String a = "a:";
for (int i=0; i<letters.length; i++) {
int temp = letters[i];
i = i+97;
if (temp == (char)i) {
temp = temp + "*";
}
i = i - 97;
}
System.out.println(temp);
}
Writing (char)97 makes the code less readable. Use 'a'.
As 3kings said in a comment, you need an array of 26 counters, one for each letter of the English alphabet.
Your code should also handle both uppercase and lowercase letters.
private static void printLetterCounts(String text) {
int[] letterCount = new int[26];
for (char c : text.toCharArray())
if (c >= 'a' && c <= 'z')
letterCount[c - 'a']++;
else if (c >= 'A' && c <= 'Z')
letterCount[c - 'A']++;
for (int i = 0; i < 26; i++)
if (letterCount[i] > 0) {
char[] stars = new char[letterCount[i]];
Arrays.fill(stars, '*');
System.out.println((char)('a' + i) + ":" + new String(stars));
}
}
Test
printLetterCounts("abaacc");
System.out.println();
printLetterCounts("This is a test of the letter counting logic");
Output
a:***
b:*
c:**
a:*
c:**
e:****
f:*
g:**
h:**
i:****
l:**
n:**
o:***
r:*
s:***
t:*******
u:*
I'm trying to create a loop which only returns letters. In my code, I get symbols that I don't want. How do I fix my loop so that when my integer is +3, it only gives me letters?
public static String caesarDecrypt(String encoded, int shift){
String decrypted = "";
for (int i = 0; i < encoded.length(); i++) {
char t = encoded.charAt(i);
if ((t <= 'a') && (t >= 'z')) {
t -= shift;
}
if (t > 'z') {
t += 26;
} else if ((t >= 'A') && (t <= 'Z')) {
t -= shift;
if (t > 'Z')
t += 26;
} else {
}
decrypted = decrypted + t;
}
}
You are subtracting the shift value from the letters. Therefore, the new letter can never be > 'z'. You should check if the it is < 'a' (or 'A', respectively).
StringBuilder decrypted = new StringBuilder(encoded.length());
for (int i = 0; i < encoded.length(); i++)
{
char t = encoded.charAt(i);
if ((t >= 'a') && (t <= 'z'))
{
t -= shift;
while (t < 'a')
{
t += 26;
}
}
else if ((t >= 'A') && (t <= 'Z'))
{
t -= shift;
while (t < 'A')
{
t += 26;
}
}
decrypted.append(t);
}
return decrypted.toString();
Also, you shouldn't be using String concatenation to generate the result. Learn about StringBuilder instead.
EDIT: To make sure the new letter is in the range 'a' .. 'z' for an arbitrary (positive) shift, you should use while instead of if.
I am not giving you exact code. But I can help you in logic:
Check whether you are reaching end points (a, A, z, Z) due to the shift.
If you exceed the end points either way, then compute the distance between end points and shifted t. Add/subtract/modulus (based on the end point) this distance to the other endpoint to get the exact letter.
Something like this? (Warning, untested)
public static String caesarDecrypt(String encoded, int shift) {
String decrypted = "";
for (int i = 0; i < encoded.length(); i++) {
char t = encoded.charAt(i).ToUpper();
decrypted = decrypted + decode(t, shift);
}
}
// call with uppercase ASCII letters, and a positive shift
function decode(char n, int shift)
{
if ((n < 'A') || (n > 'Z')) return ('-');
var str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var s = str.charAt(((n - 'A') + shift)%26);
return(s);
}
As you are naming your method caesarDecrypt (I assume you mean encrypt), I think you want a shift in the alphabet including wrapping around.
This code will do that for you:
public class Snippet {
public static void main(String[] args) {
System.out.println(caesarShift("This is a Fizzy test.", 5));
System.out.println(caesarShift("Ymnx nx f Kneed yjxy.", -5));
}
public static String caesarShift(String input, int shift) {
// making sure that shift is positive so that modulo works correctly
while (shift < 0)
shift += 26;
int l = input.length();
StringBuffer output = new StringBuffer();
for (int i = 0; i < l; i++) {
char c = input.charAt(i);
char newLetter = c;
if (c >= 'a' && c <= 'z') { // lowercase
newLetter = (char) ((c - 'a' + shift) % 26 + 'a'); // shift, wrap it and convert it back to char
} else if (c >= 'A' && c <= 'Z') { // uppercase
newLetter = (char) ((c - 'A' + shift) % 26 + 'A'); // shift, wrap it and convert it back to char
}
output.append(newLetter);
}
return output.toString();
}
}
This will handle lowercase and uppercase letters. Everything else will be left as it is (like spaces, punctuations, etc).
Please take some time to look at this code to understand how it works. I have put some comments to make it clearer. From your code I think you were a bit confused, so it is important that you understand this code very well. If you have questions, feel free to ask them.
This code
String start = "abcdefghijklmnopqrstuvwxyz";
String encrypted = caesarShift(start, 3);
String decrypted = caesarShift(encrypted, -3);
System.out.println("Start : " + start);
System.out.println("Encrypted : " + encrypted);
System.out.println("Decrypted : " + decrypted);
will give this result
Start : abcdefghijklmnopqrstuvwxyz
Encrypted : defghijklmnopqrstuvwxyzabc
Decrypted : abcdefghijklmnopqrstuvwxyz