Problem encryption exercise with ASCII table - java

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.

Related

Implementation of the Caesar cipher

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

How to fix: Number of occurrences of a letter in a string

I'm trying to count the number of occurrences of letters that are in string. The code that I have written technically does what I want, but not the way I want to do it. For example, if I input "Hello World", I want my code to return "a=0 b=0 c=0 d=0 e=1 etc...." with the code I have written it returns "H=1, e=1, l=2 etc...."
Also how would I make sure that it is not case sensitive and it doesn't count spaces.
Code:
import java.util.Scanner;
public class Sequence {
private static Scanner scan = null;
public static void main(String[] args) {
scan = new Scanner(System.in);
String str = null;
System.out.print("Type text: ");
str = scan.nextLine();
int[] count = new int[255];
int length = str.length();
for (int i = 0; i < length; i++)
{
count[str.charAt(i)]++;
}
char[] ch = new char[str.length()];
for (int i = 0; i < length; i++)
{
ch[i] = str.charAt(i);
int find = 0;
for (int j = 0; j <= i; j++)
{
if (str.charAt(i) == ch[j])
find++;
}
if (find == 1)
{
System.out.print(str.charAt(i) + "=" + count[str.charAt(i)] + " ");
}
}
}
}
As I hinted in my original comment you only need an array of 26 int(s) because there are only 26 letters in the alphabet. Before I share the code, it is important to note that Java char is an integral type (and, for example, 'a' + 1 == 'b'). That property is important, because it allows you to determine the correct offset in an array (especially if you force the input to lower case). Something like,
Scanner scan = new Scanner(System.in);
System.out.print("Type text: ");
String str = scan.nextLine();
int[] count = new int[26];
for (int i = 0; i < str.length(); i++) {
char ch = Character.toLowerCase(str.charAt(i)); // not case sensitive
if (ch >= 'a' && ch <= 'z') { // don't count "spaces" (or anything non-letter)
count[ch - 'a']++; // as 'a' + 1 == 'b', so 'b' - 'a' == 1
}
}
for (int i = 0; i < count.length; i++) {
if (count[i] != 0) {
System.out.printf("%c=%d ", 'a' + i, count[i]);
}
}
System.out.println();
If you really want to see all of the letters that have counts of zero (seems pointless to me), change
if (count[i] != 0) {
System.out.printf("%c=%d ", 'a' + i, count[i]);
}
to remove the if and just
System.out.printf("%c=%d ", 'a' + i, count[i]);
Change str = scan.nextLine(); to str = scan.nextLine().toLowerCase().replaceAll("\\s+","");
.toLowerCase() is a method which makes every char in the string lowercase.
.replaceAll() is a method which replaces one char with another. In this case, it replaces whitespaces with nothing.

Occurrences in char Array

I have this problem,
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100
at Exercise_12_2.main(Exercise_12_2.java:28)
When i am trying to just simply count the occurrences of the letters in a char array. I just cant seem to wrap my head around how to work it out. I have been at it for several hours. Please help get me on the right track.
input: a a a b b c !
Expected output:
Counts:
a 3
b 2
c 1
This is my code so far. Please help me.
import java.util.Scanner;
public class Exercise_12_2
{
public static void main(String[] args)
{
Scanner input = new Scanner(System.in); // Setup scanner
char[] charArray = new char[100];
int[] counts = new int[26];
char tempinput = '?';
System.out.print("Enter letters (or ! to quit): ");
while (tempinput != '!')
{
tempinput = (input.next()).charAt(0);
charArray[tempinput]++;
for (int c = 'a'; c <= 'z'; c++)
{
for (int k = 0; k <= charArray.length; k++)
{
if (c == charArray[k])
{
counts[c] += 1;
}
}
}
}
}
public static void displayCounts(int[] counts)
{
for (int i = 0; i < counts.length; i++)
{
if ((i + 1) % 10 == 0)
{
System.out.println(counts[i] + " " + (char)(i + 'a'));
}
else
{
System.out.print(counts[i] + " " + (char)(i + 'a') + " ");
}
}
}
}
java.lang.ArrayIndexOutOfBoundsException: 100 means you are asking for the 101st element of an array with only 100 elements. Remember, java arrays are indexed starting with 0.
From that you might be able to tell why this line is broken:
for (int k = 0; k <= charArray.length; k++)
charArray.length is 100, so you run up until k is 101, meaning that you try charArray[100] which is asking for the 101st element of charArray, but charArray only has 100 elements.
If you switch k <= charArray.length to k < charArray.length you should get farther.
On an unrelated note, counts[c] += 1; won't work. The int val of a char is its ascii value. The letter a for example is 97, so you'll go way off the end of your counts array. This also doesn't account for upper/lower case (which have different numeric values).
This problem becomes much easier if you use an appropriate data structure. For example, I would use a Map<Character, Integer> to keep the count and then iterate that to display. Like,
Scanner input = new Scanner(System.in); // Setup scanner
System.out.print("Enter letters (or ! to quit): ");
Map<Character, Integer> map = new HashMap<>();
char ch;
while ((ch = Character.toLowerCase(input.next().charAt(0))) != '!') {
map.put(ch, map.getOrDefault(ch, 0) + 1);
}
System.out.println("Counts:");
for (int c = 'a'; c <= 'z'; c++) {
if (map.containsKey((char) c)) {
System.out.printf("%c\t%d%n", c, map.get((char) c));
}
}
Which I tested with your example input, and I get as requested:
Enter letters (or ! to quit): a a a b b c !
Counts:
a 3
b 2
c 1
I think after creating the character array of [a, a, b c, d, !, ..], following code will be enough to calculate count for each alphabet [a-z].
for (int k = 0; k < charArray.length; k++)
{
char c = charArray[k];
if (c >= 'a' && c <= 'z' )
{
counts[c - 'a'] += 1;
}
}

Vignere Cipher encryption/decryption using arrays

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()].

How to use ASCII in array

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:*

Categories