Substitution code working one way but not in reverse - java

I'm trying to make a simple "translation" project as i'm still learning java. But something went wrong and I've spent many hours and still no idea how to fix it. Here is the code:
public static void main(String[] args)
{
char[] abcCode = {'!','#','#','$','%','^','&','*','(',')','_','-','+','=','|','`','~','}','{','[',']','"',';',':','>','<','?','/','\''};
char[] numCode = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','x','y','z'};
String src = args[0];
String result = "";
for(int i=0; i < src.length();i++)
{
char ch = src.charAt(i);
if ('a' <= ch && ch <= 'z')
result += abcCode[ch-'a'];
else if (abcCode[0] <= ch && ch <= abcCode[15])
result += numCode[ch-abcCode[0]];
}
System.out.println("src: "+src);
System.out.println("result: "+result);}}
For example if I type "lol" I will get "-|-", which is what I want. But the opposite never works, I'm supposed to type -|- and get "lol". Also if I type "#" and a few others I will get an error. What's the problem? Any lights?

Your problem is that the abcCode array is populated with things which, when converted to numbers, do not start at one value and go up by one in successive elements. I put the following code just after the declaration of the arrays:
for (char c: abcCode)
{
System.out.println(c - args[0].charAt(0));
}
return;
and got the following output:
-91
-60
-89
-88
-87
-30
-86
-82
-84
-83
-29
-79
-81
-63 0
-28 2 1
-1
-33
-31
-90
-65
-66
-62
-64
-61
-77
-85
(In my output, each number was on a different line, but I've forgotten how to force that here in the answer.)
As you can see, the resulting numbers are not valid indices into the other array.

Related

Strange Java Behavior [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed last year.
Improve this question
Firstly, this question came about because I was trying to make a hello world program (the first main method commented out) as convoluted as possible. I tried to rewrite some of it to make it more readable but maintain the strange behavior I am addressing.
Specifically, in the for loop I am trying to convert the strings to floats, then truncate the floats to ASCII values, and then lastly to chars that say "Hello, World!" The problem is that inside the conditional, in the for loop, if you have it simply be (i == 0) it will capitalize the h just fine but when you add
(i == 6) it acts really strange and ends up printing chars inconsistently such as "Hello,d!", "Hello,!" and "Hello,rld!" (among others). Can anyone explain this behavior to me? Thank you.
import java.util.Scanner;
class helloWorld {
// public static void main(String[] args) {
// System.out.println("Hello, World!");
// }
public static void main(String[] args) {
//This array is just soon to be truncated ASCII vals
String[] nums = {"104.367", "101.432", "108.43276", "108.1", "111.43", "44.231", "32.12", "119.32", "111.24", "114.37", "108.2", "100.52", "33.237", "10.4"};
//Empty "Hello, World!" string
String message = "";
//This is to store the converted ASCII values
char[] chars = new char[nums.length];
//this is just the difference in ASCII vals to go from lower to uppercase letters
int toUpperCase = 32;
for (int i = 0; i < nums.length; i++){
//i == 6 || i == 0 because I want the w and h to be capitalized
//i == 0 capitalizes the 'h' and prints as expected but adding "i == 6 ||" to the condition causes it to break and print chars inconsistently
if (i == 6 || i == 0) {
//converts from float to int (truncates decimals to the normal ASCII val) and then from the ASCII value to the char
chars[i] = (char)((int)((Float.parseFloat(nums[i])) - toUpperCase));
} else {
chars[i] = (char)((int)(Float.parseFloat(nums[i])));
}
System.out.print(chars[i]);
}
System.out.print(message);
}
}
tl;dr
The bug is actually a minor one, you used i == 6 but instead you should have used i == 7.
Index
Let's break down what happens and why. To start off, the source string you are reading from (even though you made it floats, but doesn't matter) is Hello, World!, the indices for your array are as follows:
// _ denotes the space
h e l l o , _ w o r l d !
0 1 2 3 4 5 6 7 8 9 10 11 12
Note that 6 is actually the index of the space and not the w, which you wanted to capitalize.
NUL character
Next up, you subtract 32 from the ASCII value of the space. Fun fact, the ASCII value of that is 32 as well:
System.out.println((int) ' '); // 32
So your subtraction results in 0. The char that has the ASCII value 0 is the so called null character (not to confuse with the Java keyword null), which is a special control character with no visual representation. It can also be written as '\0'.
System.out.println((char) 0);
So when you told your console to print the null char, it might start doing funny things. Although, the consoles I tested with, simply skip the character and print Hello,world! consistently.
If you want to try it out with a minimal example, use this one:
System.out.println("Hello,\0world!");
which is the string that your code produced.
ASCII table
While at it, here is the ASCII table with the null character and space highlighted (from Wikipedia):
You can also see that the first 32 characters are all control characters without a direct visual representation. In fact, try out this one:
System.out.print((char) 7);
depending on your console, you might hear a beep sound now.

Rotating a string with ASCII in Java

I'm doing some practice on HackerRank and I'm stuck on a problem. The problem is:
Julius Caesar protected his confidential information by encrypting it using a cipher. Caesar's cipher shifts each letter by a number of letters. If the shift takes you past the end of the alphabet, just rotate back to the front of the alphabet. In the case of a rotation by 3, w, x, y and z would map to z, a, b and c.
Original alphabet: abcdefghijklmnopqrstuvwxyz
Alphabet rotated +3: defghijklmnopqrstuvwxyzabc
Example
input:
11
middle-Outz
2
output:
okffng-Qwvb
I pass this test case, but I don't pass the rest of the test cases and it won't tell me which ones. What am I missing in my logic?
public static String caesarCipher(String s, int k) {
String newString = "";
// Write your code here
for (int i=0; i<s.length(); i++) {
char currentChar = s.charAt(i);
int currentAscii = currentChar;
if (currentAscii <65|| currentAscii>122 || (currentAscii>90 && currentAscii < 97)) {
newString += currentChar;
} else {
currentAscii = currentChar+k;
if (currentAscii > 122) {
currentAscii = Math.abs(currentAscii-26);
}
newString += (char) currentAscii;
}
}
return newString;
}
.
Here is an ASCII chart.
Your rotate algorithm is:
If the value is below 65 (less than 'A' in that chart), or above 122 (more than 'z' in that chart), do nothing to the character and just copy it verbatim.
In all other cases, add k, e.g. 11. This goes wrong, immediately. Note how for example [ is in between 65 and 122. Adding 11 to [ is nonsensical. [ should obviously be treated the same as, say, < is treated (which is less than 65).
Of course, adding 11 isn't what 'rotating' is all about: If adding 11 pushes it 'beyond' the z or Z, you want to snap around back to the a or A. Your code does that, but only if the end result is above 'z'. You forgot about 'Z'.
For example, adding 1 to Z (Which is value 90) gets you [ (the character with value 91). Given that 91 is not above 122, you don't modify this at all.
Add more test cases, especially involving capitals, preferably Z. You'll see the problem immediately.
The solution involves getting rid of the 65/122 dichotomy; instead it's 2 ranges: 65-90 and 97-122. For what it is worth, int a = (int) 65; and int a = (int) 'a'; is exactly the same code, and the latter is far more readable, so you might want to use that. There is no need to write any actual number in this code anywhere, except possibly '26'.

Counting special letters with array

So I have a program that counts the number of occurrences of each letter in a string, and for that I use
int[] charAmount = new int[30];
for(int i = 0; i<text.length(); i++){
char sign = text.charAt(i);
int value = sign;
if(value >= 97 && value <= 122){
charAmount[value-97]++; // 97 = 'a'
}
This works fine, but I also need to cover the letters 'æ' (230), 'ø' (248) & 'å' (229). How can I "assign" those three letters to the 26, 27 & 29th index of the charAmount array without using if tests or a switch?
EDIT: The code presented above is not the whole block, I also have a switch for the letters in question, but I am looking for a better solution.
BONUS PROBLEM: When I try to enter a string like "æææ" or something, the value of 'æ' is suddenly 8216. I use a Scanner to read the input.
Try this after your if:
else if (value == 230)
charAmount[26]++;
else if (value == 248)
charAmount[27]++;
else if (value == 229)
charAmount[29]++;
You can also do an array of the chars and their associations, the array will look like this:
spChars_to_Chars =
0 => 96
1 => 97
...
229 => 29
230 => 26
...
248 => 27
And then just do this in your if:
charAmount[spChars_to_Chars[value]]++;
If you can use external libraries, I would rather try using Apache commons. They provide a function to count the matches of a given substring (your characters) in a bigger string:
StringUtils.countMatches(...)

Replace numbers with string representation

I am trying to make a program, that displays rather large numbers (BigInteger large). To make it easier for the user, I am displaying the numbers as string (1 000 000 = 1 Million). My below code shows my current attempt. My problem lies, in the fact that the actual number that I am replacing will not be nice and round. This program does not need to display the 100% accurate value, rather give a ball park figure. I.E:
1 234 567 = 1 Million
1 000 000 = 1 Million
1 934 234 = 1 Million
My current code (Shortened for brevity):
if (!displayNumbers) {
StringBuffer sb = new StringBuffer(_combinations);
String t = sb.reverse().toString();
Toast.makeText(getApplicationContext(), t, 1).show();
...
if (t.contains("000 000 000 000 000")) {
t.replace("000 000 000 000 000", "quadrillion");
}
if (t.contains("000 000 000 000")) {
t.replace("000 000 000 000", "trillion");
}
if (t.contains("000 000 000")) {
t.replace("000 000 000", "billion");
}
if (t.contains("000 000")) {
t.replace("000 000", "million");
}
sb = new StringBuffer(t);
_combinations = sb.reverse().toString();
}
I was thinking something along the lines of replacing the zero's with #'s so that it would just search for x lots of 3 digits and replace with corresponding word, but I do not know how to implement this. I should also note that I am aware that the million, billion, etc are currently spelt backwards in the final output string.
EDIT: Should note for UK readers that I am using USA definitions of *illions.
EDIT 2: Been doing some more Googling and found this - java Regex: replace all numerical values with one number. Except I have no idea how to modify the Regex for finding 000 000.
you can do that using if and else statements, from what I understand you're not looking for exact representation , so it is ok to say 1 Million for 1400000.
To do so you can use code like this:
int myValue
String txt = null;
if (myValue> 500 000 000){
txt = String.ValueOf((int) myValue/1000000000) + " Billion"
} else if (myValue> 500 000){
txt = String.ValueOf((int) myValue/1000000) + " Million"
} else if (myValue> 500) {
txt = String.ValueOf((int) myValue/1000) + " Thousand"
}
This should work for the simple usage you're describing.
Your number(in text) is just for display purpose. It's bad idea to manipulate numbers after converting them to string.
Why don't you just parse the text to integer and format it with a proper way for display.
Here is a example you should take a look
I would like to thank everyone else for their help with suggestions on how to fix this, and I am sure that they are all possible. However, I found that the best way, was to still convert the BigInteger to string, but not to add whitespace.
If the string's length was less than 6 I left it as it was and simply added whitespace for looks. If it was longer then 6, I took the first 1, 2 or 3 numbers depending on the Mod of the string's length and used them as actual numbers to show at the start (1 Million, 34 Billion, 124 Quadrillion, etc).
This was then followed by seeing how long the remaining string was, and simply adding more string to the display text if it was longer then a certain amount.
If anyone needs clarification of this, please do not hesitate to ask me! Here is the link to the pastebin - http://pastebin.com/3X681UkG
Good luck!
Example implementation that rounds numbers over one thousand to one significant figure.
Optionally, groups of three digits may be separated by comma or space.
private final static String[] illions = {
"m", "b", "tr", "quadr", "quint", "sext", "sept", "oct", "non", "dec",
"undec", "duodec", "tredec", "quattuordec", "quindec", "sexdec",
"septendec", "octodec", "novemdec", "vigint", "unvigint", "duovigint",
"trevigint", "quattuorvigint", "quinvigint", "sexvigint", "septenvigint",
"octovigint", "novemvigint", "trigint", "untrigint", "duotrigint"
};
private static String approximate( String n ) {
String approx = n;
if ( n != null && n.matches( "^\\d{1,3}[\\s,]?(\\d{3}[\\s,]?)*\\d{3}$" ) ) {
n = n.replaceAll( "[\\s,]", "" );
int i = n.length() + 2;
if ( i < 105 ) {
int rnd = (int) Math.round( Double.parseDouble( n.substring( 0, 2 ) ) / 10 )
* (int) Math.pow(10, i % 3);
n = i > 8 ? illions[ i / 3 - 3 ] + "illion" : "thousand";
approx = rnd + " " + n;
}
}
return approx;
}
Test
System.out.printf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s",
approximate("999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"), // "1000 duotrigintillion"
approximate("4857476486598746598743265987426598724365987265987324598726598734625987564987456"), // "5 quinvigintillion"
approximate("56843584275874587243582837465847326554"), // "60 undecillion"
approximate("1 345 678 910 111 213"), // "1 quadrillion"
approximate("47,648,658,437,651"), // "50 trillion"
approximate("9 891 011 123"), // "10 billion"
approximate("687654321"), // "700 million"
approximate("32 456 999"), // "30 million"
approximate("2,678,910"), // "3 million"
approximate("1 234 567"), // "1 million"
approximate("123456"), // "100 thousand"
approximate("17,654"), // "20 thousand"
approximate("8765"), // "9 thousand"
approximate("654") // "654"
);
I would just simply count digits. rough code from scratch:
String result = "";
Pattern pattern = Pattern.compile("[\\s0-9]+");
Matcher matcher = pattern.matcher(t);
int index = 0;
while (matcher.find()) {
int newIndex = matcher.start();
result += t.substring(index, newIndex);
result += convert(matcher.group());
index = matcher.end() + 1;
}
result += t.substring(index, t.length() - 1);
private String convert(String uglyNumber) {
// get rid of whitespaces
String number = uglyNumber.replace("\\s", "");
// simply count digits
if( 6 < number.length() && number.length() <= 9 ) {
return "million";
} else if( 9 < number.length() && number.length() <= 12 ) {
return "million";
} else ...
return ulgyNumber;
}
if numbers are more compilcated than simple mix of digits and whitespaces, you may want to have a look for this regex site:

Java - Writing a syllable counter based on specifications

Specification for a syllable:
Each group of adjacent vowels (a, e, i, o, u, y) counts as one syllable (for example, the "ea" in "real" contributes one syllable, but the "e...a" in "regal" counts as two syllables). However, an "e" at the end of a word doesn't count as a syllable. Also each word has at least one syllable, even if the previous rules give a count of zero.
My countSyllables method:
public int countSyllables(String word) {
int count = 0;
word = word.toLowerCase();
for (int i = 0; i < word.length(); i++) {
if (word.charAt(i) == '\"' || word.charAt(i) == '\'' || word.charAt(i) == '-' || word.charAt(i) == ',' || word.charAt(i) == ')' || word.charAt(i) == '(') {
word = word.substring(0,i)+word.substring(i+1, word.length());
}
}
boolean isPrevVowel = false;
for (int j = 0; j < word.length(); j++) {
if (word.contains("a") || word.contains("e") || word.contains("i") || word.contains("o") || word.contains("u")) {
if (isVowel(word.charAt(j)) && !((word.charAt(j) == 'e') && (j == word.length()-1))) {
if (isPrevVowel == false) {
count++;
isPrevVowel = true;
}
} else {
isPrevVowel = false;
}
} else {
count++;
break;
}
}
return count;
}
The isVowel method which determines if a letter is a vowel:
public boolean isVowel(char c) {
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
return true;
} else {
return false;
}
}
According to a colleague, this should result in 528 syllables when used on this text, but I can seem to get it to equal that and I don't know which of us is correct. Please help me develop my method into the correct algorithm or help show this is correct. Thank you.
One of the problem might be that you call to lover case method on the input, but you do not assign it.
So if you change
word.toLowerCase();
to
word = word.toLowerCase();
will help for sure.
I have just invented a new way to count syllables in Java.
My new library, The Lawrence Style Checker, can be viewed here: https://github.com/troywatson/Lawrence-Style-Checker
I counted your syllables for every word using my program and displayed the results here: http://pastebin.com/LyiBTcbb
With my dictionary method of counting syllables I got: 528 syllables total.
This is the exact number the questioner gave of the correct number of syllables. Yet I still dispute this number for reasons described below:
Strike rate: 99.4% correct
Words wrong: 2 / 337 words
Words wrong and wrong syllable counts: {resinous: 4, aardwolf: 3}
Here is my code:
Lawrence lawrence = new Lawrence();
// Turn the text into an array of sentences.
String sentences = ""
String[] sentences2 = sentences.split("(?<=[a-z])\\.\\s+");
int count = 0;
for (String sentence : sentences2) {
sentence = sentence.replace("-", " "); // split double words
for (String word : sentence.split(" ")) {
// Get rid of punctuation marks and spaces.
word = lawrence.cleanWord(word);
// If the word is null, skip it.
if (word.length() < 1)
continue;
// Print out the word and it's syllable on one line.
System.out.print(word + ",");
System.out.println(lawrence.getSyllable(word));
count += lawrence.getSyllable(word);
}
}
System.out.println(count);
bam!
This should be easily doable with some Regex:
Pattern p = Pattern.compile("[aeiouy]+?\w*?[^e]");
String[] result = p.split(WHAT_EVER_THE_INPUT_IS);
result.length
Please note, that it is untested.
Not a direct answer (and I would give you one if I thought it was constructive, my count is about 238 in the last try) but I will give you a few hints that will be fundamental to creating the answer:
Divide up your problem: Read lines, then split the lines up into words, then count the syllables for each word. Afterwords, count them up for all the lines.
Think about the order of things: first find all the syllables, and count each one by "walking" through the word. Factor in the special cases afterwards.
During design, use a debugger to step through your code. Chances are pretty high you make common mistakes like the toUpperCase() method. Better find those errors, nobody will create perfect code the first time around.
Print to console (advanced users use a log and keep the silenced log lines in the final program). Make sure to mark the println's using comments and remove them from the final implementation. Print things like line numbers and syllable counts so you can visually compare them with the text.
If you have advanced a bit, you may use Matcher.find (regular expressions) using a Pattern to find the syllables. Regular expressions are difficult beasts to master. One common mistake is have them do too much in a go.
This way you can quickly scan the text. One of the things you quickly will find out is that you will have to deal with the numbers in the text. So you need to check if a word is actually a word, otherwise, by your rules, it will have at least a single syllable.
If you have the feeling you are repeating things, like the isVowel and String.contains() methods using the same set of characters, you are probably doing something wrong. Repetition in source code is code smell.
Using regexps, I counted about 238 (in the 4th go), but I haven't really checked each and every syllable (of course).
1 14
2 17
3 17
4 15
5 15
6 14
7 16
8 19
9 17
10 17
11 16
12 19
13 18
14 15
15 18
16 15
17 16
18 17
19 16
20 17
21 17
22 19
23 17
24 16
25 17
26 17
27 16
28 17
29 15
30 17
31 19
32 23
33 0
--- total ---
538
I would strongly suggest that you use Java's String API to its full ability. For example, consider String.split(String regex):
http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#split%28java.lang.String%29
This takes a String, and a regular expression, then returns an array of all the substrings, using your regular expression as a delimeter. If you make your regular expression match all consonants or whitespace, then you will end up with an array of Strings which are either empty (and therefore do not represent a consonant) or a sequence of vowels (which do represent a consonant). Count up the latter, and you will have a solution.
Another alternative which also takes advantage of the String API and regular expressions is replaceAll:
http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#replaceAll%28java.lang.String,%20java.lang.String%29
In this case, you want a regular expression which takes the form [optional anything which isn't a vowel][one or more vowels][optional anything which isn't a vowel]. Run this regular expression on your String, and replace it with a single character (eg "1"). The end result is that each syllable will be replaced by a single character. Then all you need to do is String.length() and you'll know how many syllables you had.
Depending on the requirements of your solution, these may not work. If this is a homework question relating to algorithm design, this is almost certainly not the preferred answer, but it does have the benefit of being concise and makes good use of the built-in (and therefore highly optimized) Java APIs.
private static int countSyllables(String word)
{
//System.out.print("Counting syllables in " + word + "...");
int numSyllables = 0;
boolean newSyllable = true;
String vowels = "aeiouy";
char[] cArray = word.toCharArray();
for (int i = 0; i < cArray.length; i++)
{
if (i == cArray.length-1 && Character.toLowerCase(cArray[i]) == 'e'
&& newSyllable && numSyllables > 0) {
numSyllables--;
}
if (newSyllable && vowels.indexOf(Character.toLowerCase(cArray[i])) >= 0) {
newSyllable = false;
numSyllables++;
}
else if (vowels.indexOf(Character.toLowerCase(cArray[i])) < 0) {
newSyllable = true;
}
}
//System.out.println( "found " + numSyllables);
return numSyllables;
}
Another implementation can be found at below pastebin link:
https://pastebin.com/q6rdyaEd
This is my implementation for counting syllables
protected int countSyllables(String word)
{
// getNumSyllables method in BasicDocument (module 1) and
// EfficientDocument (module 2).
int syllables = 0;
word = word.toLowerCase();
if(word.contains("the ")){
syllables ++;
}
String[] split = word.split("e!$|e[?]$|e,|e |e[),]|e$");
ArrayList<String> tokens = new ArrayList<String>();
Pattern tokSplitter = Pattern.compile("[aeiouy]+");
for (int i = 0; i < split.length; i++) {
String s = split[i];
Matcher m = tokSplitter.matcher(s);
while (m.find()) {
tokens.add(m.group());
}
}
syllables += tokens.size();
return syllables;
}
It works fine for me.

Categories