Caesar Cipher with Ascii Characters - java

I'm trying to encrypt a String using ascii characters with a key position integer. I've got two methods, one for encrypting, one for decrypting.
The problem is that when the character value is above 126, I try to mod 126 it and then add 32 back into it but I get numbers far out of these limits.
public static String encrypt(String s, int k) {
char[] arr = s.toCharArray();
int ascii;
for(int i = 0; i < arr.length; i++) {
ascii = (int) arr[i];
ascii = ascii + k;
// System.out.println(ascii);
if (ascii > 126) {
ascii = 32 + (126 % ascii);
// System.out.print("Changed: " + ascii);
}
arr[i] = (char) ascii;
}
return String.valueOf(arr);
}
The two commented out lines were for testing, and the changed values are crazy high, like instead of going from 127 % 126 = 1 + 32 for a total of 33 (intended value), I get 15870.

First, as mypetlion mentioned, the modulo operation a % b equals the remainder when a is divided by b. E.g. 11 % 4 = 3, 8 % 15 = 8. To be precise, it is the value k that satisfies b * n + k = a, b * (n + 1) > a, k < b where n is an integer. There are more appropriate descriptions using discrete maths and I'm ignoring negative arguments but they are not important. So in your case you should be doing ascii % 128.
As of why the modulo should be 128 and not 126, 7 bit ascii code goes from 0-127, 128 values in total. Also I'm a bit confused as to what the +32 is for, since you already did +k.
Also you can omit your if block completely because c % 128 = c for any c in range [0, 128).
So I would write the following code:
public static String encrypt(String s, int k) {
char[] arr = s.toCharArray();
for(int i = 0; i < arr.length; i++){
arr[i] = (char) ((arr[i] + k) % 128);
}
return String.valueOf(arr);
}
public static String decrypt(String s, int k) {
char[] arr = s.toCharArray();
for(int i = 0; i < arr.length; i++){
arr[i] = (char) ((arr[i] + 128 - k % 128) % 128);
// + 128 - k % 128 because I don't want do deal with negative numbers.
}
return String.valueOf(arr);
}
I am confident my code will work (for all positive values of k), but I cannot explain why your code produced some crazy high ascii values. When I ran your code on my IDE it didn't do that nor could I see why it would from your code.
Finally, this cipher method will encrypt and decrypt alright, but note that ascii does contain lots of control characters that are not print-friendly. So if you want to limit your cipher vocabulary to only letters and punctuations, you would need to do some character mapping to limit the characters you want to encrypt and encrypt to. This will be much more complicated, not to mention that you would have to consider problems like CR+LF in Windows v. LF in Unix. The following is a simple example that only encrypts and decrypts letters.
public static int asciiToCustom(char ascii) {
// Maps 65-90 & 97-122 to 0-51.
int customCode;
if(ascii >= 65 && ascii <= 90){
customCode = ascii - 65;
}
else if(ascii >= 97 && ascii <= 122){
customCode = ascii - 71;
}
else{
throw new RuntimeException("not a letter!");
}
return customCode;
}
public static char customToAscii(int custom) {
// Maps 0-51 to 65-90 & 97-122.
int ascii;
if(custom >= 0 && custom <= 25){
ascii = custom + 65;
}
else if(custom >= 26 && custom <= 51){
ascii = custom + 71;
}
else{
throw new RuntimeException("not a valid custom code!");
}
return (char) ascii;
}
public static String encrypt(String s, int k) {
char[] arr = s.toCharArray();
for(int i = 0; i < arr.length; i++){
if(Character.isLetter(arr[i])){
arr[i] = customToAscii((asciiToCustom(arr[i]) + k) % 52);
}
}
return String.valueOf(arr);
}
public static String decrypt(String s, int k) {
char[] arr = s.toCharArray();
for(int i = 0; i < arr.length; i++){
if(Character.isLetter(arr[i])){
arr[i] = customToAscii((asciiToCustom(arr[i]) + 52 - k % 52) % 52);
// + 52 - k % 52 because I don't want do deal with negative numbers.
}
}
return String.valueOf(arr);
}

Your modulo statement is written backwards for a start. Try:
ascii = 32 + (ascii % 126);

Related

Failing one test for Ceaser Cypher Encrpytor online judge

Hello friends I am using online judge for practice
I try this question for a long time to try pass all tests but it is failing 1 test
Here is the code
public static String caesarCypherEncryptor(String str, int key) {
char[] newLetters = new char[str.length()];
for (int i = 0; i < str.length(); i++) {
int newLetterCode = str.charAt(i) + key;
if (newLetterCode <= 122) {
newLetters[i] = (char)(newLetterCode);
} else {
newLetters[i] = (char)(96 + newLetterCode % 122);
}
}
return new String(newLetters);
}
Please point me in direction of want is reason for fail test
Think about what will happen when the passed in key is large enough such that newLetterCode % 122 is greater than 26.
newLetters[i] will not be a lowercase ASCII letter, right? Because the ASCII code for z is 122.
An easy fix for this is to simply change the line:
int newLetterCode = str.charAt(i) + key;
to
int newLetterCode = str.charAt(i) + key % 26;
This will prevent newLetterCode from ever being greater than 147. Which means newLetters[i] will always be a letter within a-z which your code currently is not enforcing.

Error with multiplying char with a number and add with char ( Big Java Ex 6.2)

Currently going through an exercise in my book, but i'm stuck ( I havent learned arrays yet, this chapter is on loops nested loops and for loops)
The first part of the problem is to take a credit card number and then sum every other number backwards
Consider 4358 9795, which should output the sum 5+7+8 + 3 = 23.
Heres my solution which isn't working
class Checkit{
private String creditNum;
private int sum;
public Checkit(String creditNum)
{
this.creditNum = creditNum;
sum = 0;
}
public int getSum()
{
for (int i = creditNum.length() ; i > 0 ; i--)
{
char ch = creditNum.charAt(i-1);
if(i%2 == 1 )
{
sum+=ch;
}
}
return sum;
}
}
public class test{
public static void main(String [] args)
{
Checkit sampleNumber = new Checkit("4358 9795");
System.out.println(sampleNumber.getSum());
}
}
I'm not exactly sure whats wrong with my logic. ch is taking all of the values of my credit card number 5,7,8,3. But for some reason the sum is messing up.
BONUS PART
Take Each number that wasn't added and double it, so 9+9 + 5 + 4, double each of those terms ( that becomes 18 + 18 + 10 + 8), and then get the sum of 1 + 8 + 1 + 8 + 1 + 0 + 8.
I tried the bonus part, but for some reason every time I get 9, 9, 5,4 and times that char value by 2, I get letters. I don't think I can multiply chars by integers, so should I do conversions? Note I didn't learn arrays yet
NEW CODE
class Checkit {
private String creditCardNum;
private int sum;
public Checkit(String creditCardNum) {
sum = 0;
this.creditCardNum = creditCardNum;
}
public int getSum() {
creditCardNum = creditCardNum.replaceAll("\\s+", "");
for (int i = creditCardNum.length(); i > 0; i--) {
char ch = creditCardNum.charAt(i - 1);
if (i % 2 == 0) {
sum += Character.getNumericValue(ch);
}
}
return sum;
}
public int doubleDigitSum() {
sum = 0;
creditCardNum = creditCardNum.replaceAll("\\s", "");
for (int i = creditCardNum.length(); i > 0; i--) {
char ch = creditCardNum.charAt(i - 1);
if (i % 2 == 1) {
int newChar = Character.getNumericValue(ch) * 2;
String newCharString = Integer.toString(newChar);
for (int j = 0; j < newCharString.length(); j++) {
char sumThis = newCharString.charAt(j);
sum += Character.getNumericValue(sumThis);
}
}
}
return sum;
}
}
public class DataSet{
public static void main(String [] args) {
Checkit data = new Checkit("4358 9795");
System.out.println(data.getSum());
System.out.println(data.doubleDigitSum());
}
}
You're adding char values rather than int values. Replace sum+=ch; with
sum += ch - '0';
The reason why this works as opposed to the original solution is that when you're adding char values to an int value, the char is converted to a decimal via its ASCII value. For example, '5' has a ASCII dec value of 53. However, '5' - '0' equals 5. More on these ASCII values can be found here, http://www.asciitable.com/
Your issue is that you are not converting the char to an integer:
public int getSum()
{
for (int i = creditNum.length() ; i > 0 ; i--)
{
char ch = creditNum.charAt(i-1);
if(i%2 == 1 )
{
sum+=ch;
}
}
return sum;
}
Fun fact that's causing your error - chars are numbers! They're simply a number that represents the ascii character code of the letter. So when you add them to a sum, Java is OK with that and just adds the corresponding number for the char '8' for example, which is 56.
To make your code work, you need to properly convert to an integer:
public int getSum()
{
for (int i = creditNum.length() ; i > 0 ; i--)
{
char ch = creditNum.charAt(i-1);
if(i%2 == 1)
{
sum+=Character.getNumericValue(ch);
}
}
return sum;
}
for (int i = creditNum.length() ; i > 0 ; i--) {
char ch = creditNum.charAt(i-1);
First iteration thru loop gets IndexOutOfBounds exception. And then you never check the initial character of the credit card number, since your termination criterion is ( i > 0 ), not (i >= 0)
And why not just use i -= 2 for the increment instead of the if-check?

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());

Algorithm to convert a String of decimal digits to BCD

I am looking a way to convert a string to BCD equivalent. I use Java, but it is not a question of the language indeed. I am trying to understand step by step how to convert a string to BCD.
For example, suppose I have the following string;
"0200" (This string has four ASCII characters, if we were in java this string had been contained in a byte[4] where byte[0] = 48, byte[1] = 50, byte[2] = 48 and byte[3] = 48)
In BCD (according this page: http://es.wikipedia.org/wiki/Decimal_codificado_en_binario):
0 = 0000
2 = 0010
0 = 0000
0 = 0000
Ok, I think the conversion is correct but I have to save this in a byte[2]. What Should I have to do? After, I have to read the BCD and convert it to the original string "0200" but first I have to resolve String to BCD.
Find a utility class to do this for you. Surely someone out there has written a BCD conversion utility for Java.
Here you go. I Googled "BCD Java" and got this as the first result. Copying code here for future reference.
public class BCD {
/*
* long number to bcd byte array e.g. 123 --> (0000) 0001 0010 0011
* e.g. 12 ---> 0001 0010
*/
public static byte[] DecToBCDArray(long num) {
int digits = 0;
long temp = num;
while (temp != 0) {
digits++;
temp /= 10;
}
int byteLen = digits % 2 == 0 ? digits / 2 : (digits + 1) / 2;
boolean isOdd = digits % 2 != 0;
byte bcd[] = new byte[byteLen];
for (int i = 0; i < digits; i++) {
byte tmp = (byte) (num % 10);
if (i == digits - 1 && isOdd)
bcd[i / 2] = tmp;
else if (i % 2 == 0)
bcd[i / 2] = tmp;
else {
byte foo = (byte) (tmp << 4);
bcd[i / 2] |= foo;
}
num /= 10;
}
for (int i = 0; i < byteLen / 2; i++) {
byte tmp = bcd[i];
bcd[i] = bcd[byteLen - i - 1];
bcd[byteLen - i - 1] = tmp;
}
return bcd;
}
public static String BCDtoString(byte bcd) {
StringBuffer sb = new StringBuffer();
byte high = (byte) (bcd & 0xf0);
high >>>= (byte) 4;
high = (byte) (high & 0x0f);
byte low = (byte) (bcd & 0x0f);
sb.append(high);
sb.append(low);
return sb.toString();
}
public static String BCDtoString(byte[] bcd) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bcd.length; i++) {
sb.append(BCDtoString(bcd[i]));
}
return sb.toString();
}
}
There's also this question: Java code or lib to decode a binary-coded decimal (BCD) from a String.
The first step would be to parse the string into an int so that you have the numeric value of it. Then, get the individual digits using division and modulus, and pack each pair of digits into a byte using shift and add (or shift and or).
Alternatively, you could parse each character of the string into an int individually, and avoid using division and modulus to get the numbers, but I would prefer to parse the entire string up front so that you discover right away if the string is invalid. (If you get a NumberFormatException, or if the value is less than 0 or greater than 9999 then it is invalid.)
Finally, once you have assembled the two individual bytes, you can put them into the byte[2].
You can use following:
//Convert BCD String to byte array
public static byte[] String2Bcd(java.lang.String bcdString) {
byte[] binBcd = new byte[bcdString.length() / 2];
for (int i = 0; i < binBcd.length; i++) {
String sByte = bcdString.substring(i*2, i*2+2);
binBcd[i] = Byte.parseByte(sByte, 16);
}
return binBcd;
}
You can try the following code:
public static byte[] hex2Bytes(String str) {
byte[] b = new byte[str.length() / 2];
int j = 0;
for (int i = 0; i < b.length; i++) {
char c0 = str.charAt(j++);
char c1 = str.charAt(j++);
b[i] = ((byte) (parse(c0) << 4 | parse(c1)));
}
return b;
}

algorithm to create a char array from permutations list in java

I'm sorry for the slightly confusing title, I'm unsure of how to phrase it.
I need to create a char array allowing for every possible permutation of a character set.
If I were to give you:
char[] charSet = {"a", "b", "c"};
BigInteger value = n; //where n is a number >= 0
char[] charArray = createCharArray(value, charSet);
How can I create charArray from value and charSet such that if I ran:
createCharArray(new BigInteger("6"), {"a", "b", "c"});
it would return {"a", "c"}
because
a=1
b=2
c=3
aa=4
ab=5
ac=6
Here's what I have so far:
private char[] createCharArray(BigInteger value, char[] charSet){
List<Character> charArray = new ArrayList<Character>();
if (value.compareTo(this.max) == 0)
System.out.println("");
BigInteger csSize = new BigInteger(String.valueOf(charSet.length));
if(this.powers.isEmpty())
this.powers.add(0, csSize.pow(0));
if(this.sumPowers.isEmpty())
this.sumPowers.add(0, csSize.pow(0));
BigInteger curPow;
int i = 1;
while((curPow = csSize.pow(i)).compareTo(value) <= -1){
if(this.powers.size() <= i)
this.powers.add(i, curPow);
if(this.sumPowers.size() <= i)
this.sumPowers.add(i, this.sumPowers.get(i-1).add(curPow));
i += 1;
}
i -= 1;
while (i >= 0 && value.compareTo(BigInteger.ZERO) >= 0){
if (i <= 1){
int charNum = value.divide(this.sumPowers.get(0)).intValue() - 1;
charArray.add(charSet[charNum]);
}
else{
int charNum = value.divide(this.sumPowers.get(i-1).subtract(BigInteger.ONE)).intValue() - 1;
charArray.add(charSet[charNum]);
}
value = value.subtract(this.powers.get(i));
i -= 1;
}
char[] returnArray = new char[charArray.size()];
int j = 0;
while(j<charArray.size()){
returnArray[j] = charArray.get(j);
j += 1;
}
return returnArray;
}
It certainly could use some help, as a value of 0 fails, values of 1 and 2 succeed, 3-8 fail, 9, 10 succeed, etc.
EDIT: To be clear, the value parameter must be able to be ANY number n > 0. This is why I've chosen BigInteger
Make a class that has two fields:
private char letter;
private int value;
public <classname>(char letter){
this.letter = letter;
value = 0;
}
//Setters and getters
Then in your main when initializing an array (via for loop) set the value to i + 1 (getting rid of 0)
for(int i = 0; i < <yourarray>.length; i ++){
//Assuming you initialized your objects before
<yourarray>[i].<setterforvalue>(i + 1);
}
And then to calculate them together:
for(int i = 0; i < <yourarray>.length; i ++){
for(int j = 0; j < <yourarray>.length; j ++){
if(<yourarray>[i] + <yourarray>[j] == <needednumber>){
//Do what you need to do with the value
}
}
}
Okay after much thinking and eventually breaking it down to using numbers 0-9 instead of characters. Here's the breakdown: Think about how regular base 10 numerals are created.
The number 194 is made up of a 4 in the ones column, a 9 in the tens, and a 1 in the hundreds. The difference between ones, tens, and hundreds is multiplication/division by 10, which is the base.
So I figured that I could mod the 194 by the base (10) to get 4, the ones. then divide by 10 to remove the ones column. mod again to get 9, then divide by 10, mod again to get 1, divide by 10. once the division creates a number that is exactly 0, we are done. This is because we cannot make a number 000194.
For my function, My base is the length of the character set, and the value is like 194 in the example above.
private static void createCharArray(BigInteger value, char[] charSet){
List<Character> charArray = new ArrayList<Character>();
BigInteger csSize = BigInteger.valueOf(charSet.length);
if (value.compareTo(BigInteger.ZERO) == 0)
charArray.add(0, charSet [0]);
else{
BigInteger modded = value.mod(csSize);
BigInteger digit = value.divide(csSize);
while (modded.compareTo(BigInteger.ZERO) != 0 || digit.compareTo(BigInteger.ZERO) != 0){
if(modded.compareTo(BigInteger.ZERO) == 0){
charArray.add(0, charSet[csSize.subtract(BigInteger.ONE).intValue()]);
value = value.subtract(BigInteger.ONE);
}
else
charArray.add(0, charSet[modded.subtract(BigInteger.ONE).intValue()]);
value = value.divide(csSize);
modded = value.mod(csSize);
digit = value.divide(csSize);
}
}
for(char c : charArray)
System.out.print(c);
System.out.println();
}
public static void main(String[] args) {
long start = System.nanoTime();
String characters = "";
characters += "0123456789";
characters += "abcdefghijklmnopqrstuvwxyz";
characters += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
characters += " !\"#$%&'()*+,-./:;<=>?#[\\]^_`{|}~";
char[] cs = characters.toCharArray();
Arrays.sort(cs);
createCharArray(new BigInteger("1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"), cs);
long total = System.nanoTime() - start;
System.out.println("Completed in: " + total + " billionths of a second");
System.out.println("Completed in: " + total/1000000 + " thousandth(s) of a second");
}
If you run this, note that that BigInteger 4 lines from the bottom is 100 characters long. On my machine, it takes only 1/1000 th of a second (1 Millisecond).

Categories