Can I improve this Pig-Latin converter? [closed] - java

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 9 years ago.
Improve this question
I'm brand-spanking new to Java and I made this little translator for PigLatin.
package stringmanipulation;
public class PigLatinConverter {
public String Convert(String word){
int position = 0;
if (!IsVowel(word.charAt(0))) {
for (int i= 0; i < word.length(); i++) {
if (IsVowel(word.charAt(i))) {
position = i;
break;
}
}
String first = word.substring(position, word.length());
String second = word.substring(0, position) + "ay";
return first + second;
} else {
return word + "way";
}
}
public boolean IsVowel(char c){
if (c == 'a')
return true;
else if(c == 'e')
return true;
else if(c == 'i')
return true;
else if(c == 'o')
return true;
else if(c == 'u')
return true;
else
return false;
}
}
Are there any improvements I can make?
Are there any nifty Java tricks that are in the newest Java version I might not be aware of? I come from a C# background.
Thank you!

I'd rewrite isVowel(char ch) as follows:
return "aeiou".indexOf(ch) != -1;
And I'd write the following instead:
// String first = word.substring(position, word.length());
String first = word.substring(position);
I'd also rename method names to follow coding convention.
And of course, being me, I'd also use regex instead of substring and for loop.
System.out.println("string".replaceAll("([^aeiou]+)(.*)", "$2$1ay"));
// ingstray
References
Java Coding Convention - Naming Convention

Disclaimer: I don't know Java.
Inverted logic is confusing please write your if statement as such:
if (IsVowel(word.charAt(0))) {
return word + "way";
} else {
for (int i= 0; i < word.length(); i++) {
// ...
return first + second;
}
You can even drop the else.
IsVowel may need to be private. It can also be rewritten using a single || chain, or as a "".indexOf (or whatever it is in Java).
Your for logic can be simplified int a short while:
while (position < word.length() && !IsVowel(word.charAt(position)) {
++position;
}

Here's a complete rewrite that makes the code more readable if you know how to read regex:
String[] words =
"nix scram stupid beast dough happy question another if".split(" ");
for (String word : words) {
System.out.printf("%s -> %s%n", word,
("w" + word).replaceAll(
"w(qu|[^aeiou]+|(?<=(w)))([a-z]*)",
"$3-$1$2ay"
)
);
}
This prints (as seen on ideone.com):
nix -> ix-nay
scram -> am-scray
stupid -> upid-stay
beast -> east-bay
dough -> ough-day
happy -> appy-hay
question -> estion-quay
another -> another-way
if -> if-way
Note that question becomes estion-quay, which is the correct translation according to Wikipedia article. In fact, the above words and translations are taken from the article.
The way the regex work is as follows:
First, all words are prefixed with w just in case it's needed
Then, skipping that w, look for either qu or a non-empty sequence of consonants. If neither can be found, then the actual word starts with a vowel, so grab the w using capturing lookbehind
Then just rearrange the components to get the translation
That is:
"skip" dummy w
|
w(qu|[^aeiou]+|(?<=(w)))([a-z]*) --> $3-$1$2ay
\ 2\_/ /\______/
\_________1_________/ 3
References
regular-expressions.info
Character class:[…], Alternation: |, Repetition:+,*, Lookaround:(?<=…), and Capturing:(…)

I know this question is well over a year old, but I thought I would put my modification of it. There are several improvements in this code.
public String convert(String word)
{
int position = 0;
while(!isVowel(word.charAt(position)))
{
++position;
}
if(position == 0)
{
return word + "-way";
}
else if(word.charAt(0) == 'q')
{
++position;
}
return word.substring(position) + "-" + word.substring(0, position) + "ay";
}
public boolean isVowel(char character)
{
switch(character)
{
case 'a': case 'e': case 'i': case 'o': case 'u':
return true;
default:
return false;
}
}
First the code will find the position of the first vowel, and then jump out of the loop. This is simpler than using a for loop to iterate through each letter and using break; to jump out of the loop. Secondly, this will match all the testcases on the Wikipedia site. Lastly, since chars are actually a limited range int, a switch statement can be used to improve performance and readability.

Not strictly an improvement as such, but Java convention dictates that methods should start with a lowercase letter.

I'm years removed from Java, but overall, your code looks fine. If you wanted to be nitpicky, here are some comments:
Add comments. It doesn't have to follow the Javadoc specification, but at least explicitly describe the accepted argument and the expected return value and perhaps give some hint as to how it works (behaving differently depending on whether the first character is a vowel)
You might want to catch IndexOutOfBoundsException, which I think might happen if you pass it a zero length string.
Method names should be lower case.
IsVowel can be rewritten return c == 'a' || c == 'e' and so on. Due to short-circuiting, the performance in terms of number of comparisons should be similar.

Is this homework? If so, tag it as such.
Unclear what expected behaviour is for "honest" or "ytterbium".
It doesn't respect capitals ("Foo" should turn into "Oofay", and AEIOU are also vowels).
It crashes if you pass in the empty string.

Related

Valid parentheses in Java

Code:
public static void main(String[] args) {
Arrays.asList("a+(b*c)-2-a", "(a+b*(2-c)-2+a)*2", "(a*b-(2+c)", "2*(3-a))", ")3+b*(2-c)(")
.stream().forEach((expression) -> {
if (replaceAll(expression, "[(]") == replaceAll(expression, "[)]")) {
System.out.println("correct");
} else {
System.out.println("incorrect");
}
});
}
private static int replaceAll(String word, String regex) {
int count = word.length() - word.replaceAll(regex, "").length();
return count;
}
I have to check if the expression is valid or not. What determine if an expression is valid or not are the parentheses. If it's self closed, is valid, otherwise, not.
My code is almost correct, it's printing:
correct
correct
incorrect
incorrect
correct
But it must print
correct
correct
incorrect
incorrect
incorrect -> the last expression isn't valid.
You need not only to check if the number of opening parentheses matches the number of closed, but also if each closing parenthesis goes after opening one which isn't "closed" yet:
static boolean checkParentheses(String s) {
int opened = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(')
opened++;
else if (s.charAt(i) == ')') {
if (opened == 0) // means that all parentheses are "closed" yet
return false;
opened--;
}
}
return opened == 0;
}
If you strictly need regex to be involved, do the following:
static boolean checkParentheses(String s) {
// capture a text starting with one opening parenthesis,
// ending with one closing and having no parentheses inside
Pattern p = Pattern.compile("\\([^()]*\\)");
Matcher m;
while ((m = p.matcher(s)).find())
s = m.replaceAll("");
return !(s.contains("(") || s.contains(")"));
}
Your issue is that it's not enough just to count parentheses; you also need to spot where a ')' comes too early. For example ")(" is not valid even though there are an equal number of opening and closing parentheses.
One approach is to keep a count. Start at zero. Each time you see '(', count++. Each time you see ')', count--.
After a decrement, if(count<0) the input is invalid.
At the end of input, if(count!0) the input is invalid.
It's been pointed out that this can't be done in a single regex. That's because a regex represents a finite state machine. count could in principle increase infinitely.
If you pick a maximum nesting depth, you can write a regex to check it. For example, for a maximum depth of 3:
x*(<x*(<x*(<x*>)*x*>)*x*>)*x*
(I've used 'x' instead of arbitrary chars here, for readability. Replace it with [^<>] to actually match other chars. I've also used <> instead of \(\) again for readability. The () here are for grouping.).
You can always make it work one level deeper by replacing the x* in the middle with x*(<x*>)*x* -- but you can never made a regex that doesn't stop working at a certain depth.
An alternative method is closer to what a real statement parser would do with nested structures: recurse. Something like (pseudocode):
def consumeBlock() {
switch(next char)
case end-of-input
throw error -- reached end of input inside some parentheses
case '('
consumeBlock() -- go down a nesting level
break;
case ')'
return -- go up a nesting level
default
It's an uninteresting character. Do nothing.
(a real parser compiler would do something more interesting)
}
Here consumeBlock() assumes you've just consumed a '(' and you intend to read until its pair.
Some of your inputs don't begin with a '(', so prime it by first appending a ')' to the end, as the pair to a "silent" ')' you're saying it's already consumed.
The pseudocode already shows that if you hit end-of-input mid-block, it's invalid input. Also if you are not at end-of-input when the top-level call to consumeBlock() returns, it's invalid input.
You could go through it char by char and using a counter to tell the parenthesis level in the statement.
boolean valid = true;
int level = 0;
for(int i=0; i < expr.length(); i++) {
if(expr.charAt(i) == '(') level++;
if(expr.charAt(i) == ')') level--;
if(level < 0) { // ) with no (
valid = false;
break;
}
}
if(level > 0) valid = false; // ( with no )
return valid; // true if level returned to 0

Finding if a specific character exists at a specific index

so I'm trying to figure out how to create a condition for my decision structure. I'm making a program that converts military time to conventional time. When times are entered, they must be in a XX:XX format, where X equals a digit. I'm wondering, how can I make a validation that checks to make sure that ":" always exists as a colon and is in the same spot?
myString.charAt(5) == ':'
Just change 5 to whatever you need, and check string length before you do this, so you don't ask for something past the end of a short string.
You could search the string with indexOf(), which returns the index at which the character is found. This will check if a colon is in the string and if it's in the right position.
if("10:00".indexOf(':') == 2)
// do something
It will return -1 if the value is not found. Check out the java documentation for more information.
Here is the answer for it
Suppose you had string that contains XX:XX
yourString="XX:XX"
yourString.contains(":") - return true - if exists
":" takes the 3rd position. So it will be 2(0,1,2)
yourString.charAt(2) == ':'
Together , it will be
if(yourString.contains(":") && yourString.charAt(2) == ':'){
//your logic here
}else{
something here
}
Here is one approach to that,
String[] test = new String[] { "00:00", "NN:NN", "12,13", "12:13" };
for (String fmt : test) {
boolean isValid = true;
for (int i = 0; i < fmt.length(); i++) {
char c = fmt.charAt(i);
if (i == 2) {
if (c != ':') { // here is the colon check.
isValid = false;
break;
}
} else {
// This checks for digits!
if (!Character.isDigit(c)) {
isValid = false;
break;
}
}
}
System.out.println(fmt + " is " + isValid);
}
Output is
00:00 is true
NN:NN is false
12,13 is false
12:13 is true
#WillBro posted a good answer but I give you another option:
myString.indexOf(":") == 5
With indexOf you can also look for full String inside a String.
http://docs.oracle.com/javase/7/docs/api/java/lang/String.html
But for string format validations I suggest you to use Regular Expresions.
Here's a full example that does what you want to do:
http://www.mkyong.com/regular-expressions/how-to-validate-time-in-24-hours-format-with-regular-expression/

Stack, Parentheses Matching [duplicate]

This question already has answers here:
Handling parenthesis while converting infix expressions to postfix expressions
(2 answers)
Closed 5 years ago.
So I'm working on some homework with PostFix and Infix Expressions. I'm running into a bit of a problem and can't seem to find where I'm having the issue. I can get the Infix to Postfix working...for the most part. Some of the equations I'm getting a ( or ) printed when I don't want it to be printed. Also when I have no matching parentheses I don't get an error like I want it to.
public String Infix(String equation) throws Exception{
Stack stack=new Stack();
boolean parensMatch=false;
int priority=0;
String temp="";
for(int i=0; i<equation.length(); i++){
char c=equation.charAt(i);
//if the character is equal to left paren, push
if(c=='('){
stack.push(c);
}
//if the character is equal to right paren, we start popping until we find a match
else if(c==')'){
try{
while(stack.peek()!='('){
temp+=stack.pop();
}
if(stack.peek()=='('){
char ch=stack.pop();
parensMatch=true;
}
if(parensMatch==false){
throw new Exception("Parens Not Match Error");
}
}catch(Exception e){
System.out.println(e);
}
parensMatch=false;
}
//if the character is equal to an operator, we do some extra work
//to figure out what is going to happen
else if(c=='+' || c=='-' || c=='*' || c=='/' || c=='^'){
char top=stack.peek();
if(top=='^')
priority=2;
else if(top=='*' || top=='/')
priority=1;
else
priority=0;
if(priority==2){
if(c=='*' || c=='/'){
temp+=stack.pop();
}
else if(c=='+' || c=='-'){
temp+=stack.pop();
}
else{
temp+=stack.pop();
}
}
else{
if(c=='*' || c=='/'){
temp+=stack.pop();
stack.push(c);
}
else if(c=='+' || c=='-'){
stack.push(c);
}
else{
stack.push(c);
}
}
}
//if the character is a space, we ignore it and move on
else if(c==' '){
;
}
//if the character is a letter, we add it to the string
else{
temp+=c;
}
}
int len = stack.size();
for (int j = 0; j < len; j++)
temp+=stack.pop();
return temp;
}
This is my Infix to Postfix method
(((A + B) - (C - D)) / (E - F)) This is one of the expressions that I need to solve, and AB+CD--(EF-/ is what I get when it prints to the screen. ((A is another, this one should give me an error but A(( is printed to the screen.
I have been running the debug for quite a while and can't seem to get anywhere.
Any help would be very helpful. I know it has something to with the code posted but I can't find the logic error. Thanks in advance!
So I added a new function to help with matching parens that I think will be useful. It takes the equation and just counts to see if they match or not.
public static int matchingParens(String equation){
int match=0;
for(int i=0; i<equation.length(); i++){
char c=equation.charAt(i);
if(c=='(')
match++;
else if(c==')')
match--;
else
;
}
return match;
}
To validate if parenthesis are all matched up, you can run through your String input of the math expression with a counter of initial value of 0, and if you find a (, increment your counter by 1, and if you find a ), decrement your counter by 1. If the counter ever reaches -1, break out, as it isn't a valid parenthesis match. In the end, you should have the counter's value as 0. If not, you have a mismatched parenthesis.
For the infix to postfix case, here's a standard algorithm:
Define a stack
Go through each character in the string
If it is between 0 to 9, append it to output string.
If it is left brace push to stack
If it is operator *,+,- or / then
If the stack is empty push it to the stack
If the stack is not empty then start a loop:
If the top of the stack has higher precedence
Then pop and append to output string
Else break
Push to the stack
If it is right brace then
While stack not empty and top not equal to left brace
Pop from stack and append to output string
Finally pop out the left brace.
Well I would like give you some "software engineering" tip, which in the end can solve your problem and you can learn a lot by doing it "nice".
Mathematical expression, no matter if you write them in pre, in, post order, looks always the same, if you store them in some tree structure.
Then, if you want to print that tree structure into String, you just go through the whole tree, and it varies only in the moment (and additional braces for some cases), when you write it to that string.
Preorder do it when you first go into that node, Inorder when you finished left child and Postorder, if you do it when you leaving that node.
My advice :
Class : Expression, UnaryExpression extends Expression, BinaryExpression extedns Expression and then you can make numbers and operators : Add extends BinaryExpression etc.
Then you should have class Tree, which stores the Expression root and it has methods printPreOrder(), printInOrder(), printPostOrder()
To create that tree, it would be really nice to use a Builder pattern which can be used like this :
public class Director {
private IExpressionBuilder builder;
public ArithmeticExpression construct(String text){
for (int i=0;i<text.length();i++){
if (text.charAt(i) == '+'){
builder.buildAddOperator();
}
...
}
}
And then you create concrete Builder classes, which look like this :
public class InOrderBuilder implements IExpressionBuilder {
public void buildAddOperator() {
...
}
....
}

Using regex to validate a field

Sorry for being new to programming. Apparently I'm expected to know everything about the topic before posting here. I've only been doing this 3 weeks, learning as I go.
I've got a field limited to 5 characters I'm trying to validate. The first character must be a letter, and the following 4 characters must be numbers. Regex is like Greek to me at this point, so I'm having trouble. I've been able to get the first character validated, but I'm stumped on the remaining 4. Here's my code:
if (carID.substring(0, 1).matches("[0-9]")) {
showDataFormatError();
break;
} else {
if (carID.substring(1, 5).matches("[a-zA-Z]")) {
showDataFormatError();
break;
}
}
Updating to demonstrate my horrible coding at this point.
if (carID.length() < 5) {
showDataLengthError();
break;
} else {
if (carID.matches("^[a-zA-Z][0-9]{4}$")) {
showDataFormatError();
break;
} else {
if (carYearString.length() < 0) {
showDateLengthError();
break;
} else {
try {
int carYear = Integer.parseInt(carYearString);
int currentYear = Calendar.getInstance().get(
Calendar.YEAR);
// etc.
Try this
boolean valid = carID.matches("^[a-zA-Z][0-9]{4}$");
And see the tutorial
EDIT thanks to the down voter I spotted and fixed the error.
If you want to make sure the entire string is that format, use:
^[a-zA-Z]\d{4}$
this will help you and for tutorial you should have to go through this link
Regular Expression
I would do something like..
\pL Matches Any kind of letter from any language.
\pN{4} Matches Any kind of numeric character in any script. (4 times) greedy.
...
if (carID.length() > 5) throw IllegalArgumentException();
if (carID.matches("^\pL\pN{4}$")){
return true;
} else {
return false;
...
}

Is there an existing library method that checks if a String is all upper case or lower case in Java?

I know there are plenty of upper() methods in Java and other frameworks like Apache commons lang, which convert a String to all upper case.
Are there any common libraries that provide a method like isUpper(String s) and isLower(String s), to check if all the characters in the String are upper or lower case?
EDIT:
Many good answers about converting to Upper and comparing to this. I guess I should have been a bit more specific, and said that I already had thought of that, but I was hoping to be able to use an existing method for this.
Good comment about possible inclusion of this in apache.commons.lang.StringUtils.
Someone has even submitted a patch (20090310). Hopefully we will see this soon.
https://issues.apache.org/jira/browse/LANG-471
EDIT:
What I needed this method for, was to capitalize names of hotels that sometimes came in all uppercase. I only wanted to capitalize them if they were all lower or upper case.
I did run in to the problems with non letter chars mentioned in some of the posts, and ended up doing something like this:
private static boolean isAllUpper(String s) {
for(char c : s.toCharArray()) {
if(Character.isLetter(c) && Character.isLowerCase(c)) {
return false;
}
}
return true;
}
This discussion and differing solutions (with different problems), clearly shows that there is a need for a good solid isAllUpper(String s) method in commons.lang
Until then I guess that the myString.toUpperCase().equals(myString) is the best way to go.
Now in StringUtils isAllUpperCase
This if condition can get the expected result:
String input = "ANYINPUT";
if(input.equals(input.toUpperCase())
{
// input is all upper case
}
else if (input.equals(input.toLowerCase())
{
// input is all lower case
}
else
{
// input is mixed case
}
Not a library function unfortunately, but it's fairly easy to roll your own. If efficiency is a concern, this might be faster than s.toUpperCase().equals(s) because it can bail out early.
public static boolean isUpperCase(String s)
{
for (int i=0; i<s.length(); i++)
{
if (!Character.isUpperCase(s.charAt(i)))
{
return false;
}
}
return true;
}
Edit: As other posters and commenters have noted, we need to consider the behaviour when the string contains non-letter characters: should isUpperCase("HELLO1") return true or false? The function above will return false because '1' is not an upper case character, but this is possibly not the behaviour you want. An alternative definition which would return true in this case would be:
public static boolean isUpperCase2(String s)
{
for (int i=0; i<s.length(); i++)
{
if (Character.isLowerCase(s.charAt(i)))
{
return false;
}
}
return true;
}
Not that i know.
You can copy the string and convert the copy to lower/upper case and compare to the original one.
Or create a loop which checks the single characters if the are lower or upper case.
This method might be faster than comparing a String to its upper-case version as it requires only 1 pass:
public static boolean isUpper(String s)
{
for(char c : s.toCharArray())
{
if(! Character.isUpperCase(c))
return false;
}
return true;
}
Please note that there might be some localization issues with different character sets. I don't have any first hand experience but I think there are some languages (like Turkish) where different lower case letters can map to the same upper case letter.
Guava's CharMatchers tend to offer very expressive and efficient solutions to this kind of problem.
CharMatcher.javaUpperCase().matchesAllOf("AAA"); // true
CharMatcher.javaUpperCase().matchesAllOf("A SENTENCE"); // false
CharMatcher.javaUpperCase().or(CharMatcher.whitespace()).matchesAllOf("A SENTENCE"); // true
CharMatcher.javaUpperCase().or(CharMatcher.javaLetter().negate()).matchesAllOf("A SENTENCE"); // true
CharMatcher.javaLowerCase().matchesNoneOf("A SENTENCE"); // true
A static import for com.google.common.base.CharMatcher.* can help make these more succinct.
javaLowerCase().matchesNoneOf("A SENTENCE"); // true
Try this, may help.
import java.util.regex.Pattern;
private static final String regex ="^[A-Z0-9]"; //alpha-numeric uppercase
public static boolean isUpperCase(String str){
return Pattern.compile(regex).matcher(str).find();
}
with this code, we just change the regex.
I realise that this question is quite old, but the accepted answer uses a deprecated API, and there's a question about how to do it using ICU4J. This is how I did it:
s.chars().filter(UCharacter::isLetter).allMatch(UCharacter::isUpperCase)
If you expect your input string to be short, you could go with myString.toUpperCase().equals(myString) as you suggested. It's short and expressive.
But you can also use streams:
boolean allUpper = myString.chars().noneMatch(Character::isLowerCase);
You can use java.lang.Character.isUpperCase()
Then you can easily write a method that check if your string is uppercase (with a simple loop).
Sending the message toUpperCase() to your string and then checking if the result is equal to your string will be probably slower.
Here's a solution I came up with that's a bit universal as it doesn't require any libraries or special imports, should work with any version of Java, requires only a single pass, and should be much faster than any regex based solutions:
public static final boolean isUnicaseString(String input) {
char[] carr = input.toCharArray();
// Get the index of the first letter
int i = 0;
for (; i < carr.length; i++) {
if (Character.isLetter(carr[i])) {
break;
}
}
// If we went all the way to the end above, then return true; no case at all is technically unicase
if (i == carr.length) {
return true;
}
// Determine if first letter is uppercase
boolean firstUpper = Character.isUpperCase(carr[i]);
for (; i < carr.length; i++) {
// Check each remaining letter, stopping when the case doesn't match the first
if (Character.isLetter(carr[i]) && Character.isUpperCase(carr[i]) != firstUpper) {
return false;
}
}
// If we didn't stop above, then it's unicase
return true;
}

Categories

Resources