I'm going through Project Euler Problem 059.
How to find a valid message out of decrypted possibilities from that textfile?
After turning the input into an array, performing XOR with the three-letter key and casting (char) on the results, none of the messages can get past the isLegible check.
try {
// Read the contents of the provided text file
// and convert them into an int[] array of encrypted ASCII codes
Scanner fileRead = new Scanner(new File("p059_cipher.txt"));
StringBuilder input = new StringBuilder();
while (fileRead.hasNext()) {
input.append(fileRead.next());
}
String[] parts = input.toString().split("[,]");
int[] elements = new int[parts.length];
for (int i = 0; i < parts.length; i++)
elements[i] = Integer.parseInt(parts[i]);
char[] key;
char[] convertedParts;
// Loop through possible keys,
// from ['a', 'a', 'a'] to ['z', 'z', 'z']
for (char lc1 = 'a'; lc1 <= 'z'; lc1++) {
for (char lc2 = 'a'; lc2 <= 'z'; lc2++) {
for (char lc3 = 'a'; lc3 <= 'z'; lc3++) {
key = new char[]{lc1, lc2, lc3};
// XOR each of the ASCII code chars,
// using the appropriate key letter
convertedParts = new char[elements.length];
for (int done = 0; done < elements.length; done++) {
convertedParts[done] =
(char) (elements[done] ^ key[done%3]);
}
// If the decrypted message counts as an answer,
// print out the sum of ASCII values
// of decrypted characters
if (isLegible(convertedParts))
System.out.println(getAsciiSum(convertedParts));
}
}
}
} catch (java.io.FileNotFoundException e) {
e.printStackTrace();
}
}
// Check whether or not the decoded message
// consists only of valid characters
private static boolean isLegible(char[] characters) {
// 32-34: SPACE, '!', '"'
// 40-41: '(', ')'
// 44-46: '.', '-', ','
// 48-59: '0'-'9', ':', ';'
// 63: '?'
// 65-90: 'A'-'Z'
for (char character : characters) {
int value = (int) character;
if ( ! ( (value >= 32 && value <= 34)
|| (value >= 40 && value <= 41)
|| (value >= 44 && value <= 46)
|| (value >= 48 && value <= 59)
|| (value == 63)
|| (value >= 65 && value <= 90) ) ) {
return false;
}
}
return true;
}
// Sums up the ASCII values of characters
private static long getAsciiSum(char[] characters) {
long sum = 0;
for (char character : characters)
sum += (int)(character);
return sum;
}
I expect the output in the form of legible text, but none can be found when looping through the decryptions.
Related
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
I'm trying to write a non-recursive Java method called showStar, which takes a string, and generates ALL possible combinations of that string without the mask “*” characters.
receiving this as an input "1011*00*10",
the method `showStar` will display output like:
1011000010
1011000110
1011100010
1011100110
I tried this way, however, as soon as the number of possible cases is more than the String length, the output is not exact.
Here is my code.
public static void showStar(String s){
String save;
int count = 0;
int poss;
save = s.replace('*','0');
StringBuilder myString = new StringBuilder(save);
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '*' && myString.charAt(i) == '0') {
myString.setCharAt(i, '1');
System.out.println(myString);
}
}
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '*' && myString.charAt(i) == '1') {
myString.setCharAt(i, '0');
System.out.println(myString);
}
}
}
Say there are k *s. Then there are 2^k solutions. You can generate these by copying the bits from the integers 0 - 2^k-1 in order. (adding sufficient leading zeroes)
E.g. 1**1:
0 = 00 => 1001
1 = 01 => 1011
2 = 10 => 1101
3 = 11 => 1111
Here a recursive algoritm works just perfectly:
You check if an input string contains an asterisk '*' by using an x = str.indexOf('*');
If no asterisk is present (x == -1), you just print the string and return
Otherwise, you replace the asterisk at the position to '0' and '1' and call showStar() recursively for both replacements
public static void showStar(String str) {
int x = str.indexOf('*');
if(x == -1) {
System.out.println(str);
return;
}
String prefix = str.substring(0, x);
String suffix = str.substring(x + 1);
for (char i = '0'; i <= '1'; i++) {
showStar(prefix + i + suffix);
}
}
Update
In non-recursive implementation we need to collect the asterisk positions, then prepare a binary representation and set appropriate bits at the known positions:
public static void showStar(String str) {
int[] xs = IntStream.range(0, str.length())
.filter(i -> str.charAt(i) == '*')
.toArray();
int num = (int) Math.pow(2, xs.length); // 2^n variants for n asterisks
String format = xs.length > 0 ? "%" + xs.length + "s" : "%s"; // fix if no '*'
for (int i = 0; i < num; i++) {
String bin = String.format(format, Integer.toBinaryString(i))
.replace(' ', '0'); // pad leading zeros
StringBuilder sb = new StringBuilder(str);
// set 0 or 1 in place of asterisk(s)
for (int j = 0; j < xs.length; j++) {
sb.setCharAt(xs[j], bin.charAt(j));
}
System.out.println(sb);
}
}
I want to distinguish Unicode characters and ASCII characters from the below string:
abc\u263A\uD83D\uDE0A\uD83D\uDE22123
How can I distinguish characters? Can anyone help me with this issue? I have tried some code, but it crashes in some cases. What is wrong with my code?
The first three characters are abc, and the last three characters are 123. The rest of the string is Unicode characters. I want to make a string array like this:
str[0] = 'a';
str[1] = 'b';
str[2] = 'c';
str[3] = '\u263A\uD83D';
str[4] = '\uDE0A\uD83D';
str[5] = '\uDE22';
str[6] = '1';
str[7] = '2';
str[8] = '3';
Code:
private String[] getCharArray(String unicodeStr) {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < unicodeStr.length(); i++) {
if (unicodeStr.charAt(i) == '\\') {
list.add(unicodeStr.substring(i, i + 11));
i = i + 11;
} else {
list.add(String.valueOf(unicodeStr.charAt(i)));
}
}
return list.toArray(new String[list.size()]);
}
ASCII characters exist in Unicode, they are Unicode codepoints U+0000 - U+007F, inclusive.
Java strings are represented in UTF-16, which is a 16-bit byte encoding of Unicode. Each Java char is a UTF-16 code unit. Unicode codepoints U+0000 - U+FFFF use 1 UTF-16 code unit and thus fit in a single char, whereas Unicode codepoints U+10000 and higher require a UTF-16 surrogate pair and thus need two chars.
If the string has UTF-16 code units represented as actual char values, then you can use Java's string methods that work with codepoints, eg:
private String[] getCharArray(String unicodeStr) {
ArrayList<String> list = new ArrayList<>();
int i = 0, j;
while (i < unicodeStr.length()) {
j = unicodeStr.offsetByCodePoints(i, 1);
list.add(unicodeStr.substring(i, j));
i = j;
}
return list.toArray(new String[list.size()]);
}
On the other hand, if the string has UTF-16 code units represented in an encoded "\uXXXX" format (ie, as 6 distinct characters - '\', 'u', ...), then things get a little more complicated as you have to parse the encoded sequences manually.
If you want to preserve the "\uXXXX" strings in your array, you could do something like this:
private boolean isUnicodeEncoded(string s, int index)
{
return (
(s.charAt(index) == '\\') &&
((index+5) < s.length()) &&
(s.charAt(index+1) == 'u')
);
}
private String[] getCharArray(String unicodeStr) {
ArrayList<String> list = new ArrayList<>();
int i = 0, j, start;
char ch;
while (i < unicodeStr.length()) {
start = i;
if (isUnicodeEncoded(unicodeStr, i)) {
ch = (char) Integer.parseInt(unicodeStr.substring(i+2, i+6), 16);
j = 6;
}
else {
ch = unicodeStr.charAt(i);
j = 1;
}
i += j;
if (Character.isHighSurrogate(ch) && (i < unicodeStr.length())) {
if (isUnicodeEncoded(unicodeStr, i)) {
ch = (char) Integer.parseInt(unicodeStr.substring(i+2, i+6), 16);
j = 6;
}
else {
ch = unicodeStr.charAt(i);
j = 1;
}
if (Character.isLowSurrogate(ch)) {
i += j;
}
}
list.add(unicodeStr.substring(start, i));
}
return list.toArray(new String[list.size()]);
}
If you want to decode the "\uXXXX" strings into actual chars in your array, you could do something like this instead:
private boolean isUnicodeEncoded(string s, int index)
{
return (
(s.charAt(index) == '\\') &&
((index+5) < s.length()) &&
(s.charAt(index+1) == 'u')
);
}
private String[] getCharArray(String unicodeStr) {
ArrayList<String> list = new ArrayList<>();
int i = 0, j;
char ch1, ch2;
while (i < unicodeStr.length()) {
if (isUnicodeEncoded(unicodeStr, i)) {
ch1 = (char) Integer.parseInt(unicodeStr.substring(i+2, i+6), 16);
j = 6;
}
else {
ch1 = unicodeStr.charAt(i);
j = 1;
}
i += j;
if (Character.isHighSurrogate(ch1) && (i < unicodeStr.length())) {
if (isUnicodeEncoded(unicodeStr, i)) {
ch2 = (char) Integer.parseInt(unicodeStr.substring(i+2, i+6), 16);
j = 6;
}
else {
ch2 = unicodeStr.charAt(i);
j = 1;
}
if (Character.isLowSurrogate(ch2)) {
list.add(String.valueOf(new char[]{ch1, ch2}));
i += j;
continue;
}
}
list.add(String.valueOf(ch1));
}
return list.toArray(new String[list.size()]);
}
Or, something like this (per https://stackoverflow.com/a/24046962/65863):
private String[] getCharArray(String unicodeStr) {
Properties p = new Properties();
p.load(new StringReader("key="+unicodeStr));
unicodeStr = p.getProperty("key");
ArrayList<String> list = new ArrayList<>();
int i = 0;
while (i < unicodeStr.length()) {
if (Character.isHighSurrogate(unicodeStr.charAt(i)) &&
((i+1) < unicodeStr.length()) &&
Character.isLowSurrogate(unicodeStr.charAt(i+1)))
{
list.add(unicodeStr.substring(i, i+2));
i += 2;
}
else {
list.add(unicodeStr.substring(i, i+1));
++i;
}
}
return list.toArray(new String[list.size()]);
}
It's not entirely clear what you're asking for, but if you want to tell if a specific character is ASCII, you can use Guava's ChatMatcher.ascii().
if ( CharMatcher.ascii().matches('a') ) {
System.out.println("'a' is ascii");
}
if ( CharMatcher.ascii().matches('\u263A\uD83D') ) {
// this shouldn't be printed
System.out.println("'\u263A\uD83D' is ascii");
}
I have a large string I need to convert all the non alphanumeric chars to unicode
For example
Input string : abc12/dad-das/das_sdj
Output String : abc12:002Fdad:002Ddas:002Fdas:002Fsdj
Currently I am using this function
for (char c : str.toCharArray()) {
System.out.printf(":%04X \n", (int) c);
}
Is there a better way to do it ?
Here are two ways to do it:
// Looping over string characters
private static String convert(String input) {
StringBuilder buf = new StringBuilder(input.length() + 16);
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
buf.append(c);
else
buf.append(String.format(":%04X", (int) c));
}
return buf.toString();
}
// Using regular expression
private static String convert(String input) {
StringBuffer buf = new StringBuffer(input.length() + 16);
Matcher m = Pattern.compile("[^a-zA-Z0-9]").matcher(input);
while (m.find())
m.appendReplacement(buf, String.format(":%04X", (int) m.group().charAt(0)));
return m.appendTail(buf).toString();
}
Test
System.out.println(convert("abc12/dad-das/das_sdj"));
Output
abc12:002Fdad:002Ddas:002Fdas:005Fsdj
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