tags:

views:

273

answers:

5

My application will basically accept user input via the main(String[] args) method. I have been trying to figure a way to do some checking with that input using a patter matcher.

Each valid input consists basically of a single character: (blanks should be ignored)

* 'R' or 'r' - replay
* 'Q' or 'q' - quit
* [1-6] - perform tasks 1 .. 6

If the user input is invalid - which means if we detect anything different than the above, then the application will quit after displaying an error message.

So, all the logic within the main method would be in a single big conditional:

if(Pattern.matches("[RQrq1-6]*", args[0]){

}

I have some difficulty grasping the matches method with regex involved (The above doesn't work) and I would like to avoid having multiple if-else conditions to check each valid input individually.

Can this be achieved with pattern matcher with range checking at the same time?

Thanks for any hints!

+2  A: 

I would be tempted not to do this with a regexp, since it'll only match and extract a particular pattern, and not really help with deciding what to do for a particular string (using one regexp).

Instead, I'd check out a command line option library such as Apache Commons CLI. It's much more appropriate for what you require.

Brian Agnew
I would go with Commons CLI as well
Ravi Wallau
+1  A: 

I think you need to match each argument passed in. So:

for(int i = 0; i < args.length; i++) {
   if (Pattern.matches("[RQrq1-6]+", args[i]){
     <do stuff>
   }
}
Myles
Shouldn't it be args[i] inside your for loop?
Ravi Wallau
Good call, edited.
Myles
That would match an empty string as well. Better use `+` instead of `*`.
Bart Kiers
I guess it helps to look at the regex too.
Myles
A: 

The following class works fine for me (exactly as I'd expect anyway):

public class Test{
  public static void main(String[] args) throws Exception {
    if(java.util.regex.Pattern.matches("[RQrq1-6]*", args[0])){
      System.out.println("OK: "+args[0]);
    } else {
      System.out.println("Not OK: " +args[0]);
    }
  }
}

Example:

burner@firefighter:~$ java Test rr
OK: rr
burner@firefighter:~$ java Test rrRR
OK: rrRR
burner@firefighter:~$ java Test rrRRa
Not OK: rrRRa

Now, you're probably trying to match against all characters on the command line. The command interpreter separates those out into separate strings before launching your application. You could do as Myles suggests, but then you'll process some commands before detecting bad input. You could do this instead:

String myargs = "";
for (String arg : args) {
  myargs += arg;
}
if(java.util.regex.Pattern.matches("[RQrq1-6]*", myargs)){
...
Suppressingfire
Thanks, I would like to make the same point as with the other answer. If the user passess rr, rR, or RR, the matcher should fail since we only accept one character per input, right?
denchr
Also, blanks should be ignored too.
denchr
Yes, both fair points. I believe those points had been left out of the original problem description, though :-)
Suppressingfire
+3  A: 

Your regex isn't quite right.

When you write: [RQrq1-6]*

You're saying match zero or more repetitions of any character in the square brackets...

Try this [RQrq1-6] - which means match exactly one.

I wrote a little "spike" program:

import java.util.regex.Pattern;

public class Spike {

public static void main(String[] args) {
 testIt("Q");
 testIt("q");
 testIt("z");
 testIt("Z");
 testIt("R");
 testIt("r");
 testIt("0");
 testIt("1");
 testIt("2");
 testIt("3");
 testIt("4");
 testIt("5");
 testIt("6");
 testIt("7");
 testIt("02");
 testIt("11");
 testIt("17");
 testIt(""); // should fail, used to pass with the question's regex

}

public static void testIt(String sample) {
 System.out.print(sample+" ");
 if (Pattern.matches("[RQrq1-6]*", sample)) {
  System.out.println("match");
 } else {
  System.out.println("fail");
 }
}

}

Output was this:

Q match
q match
z fail
Z fail
R match
r match
0 fail
1 match
2 match
3 match
4 match
5 match
6 match
7 fail
02 fail
11 match
17 fail
 match

Having removed the star in the regex:

Q match
q match
z fail
Z fail
R match
r match
0 fail
1 match
2 match
3 match
4 match
5 match
6 match
7 fail
02 fail
11 fail
17 fail
 fail

How to ignore the blanks

Substitute this version of testit:

public static void testIt(String sample) {
 System.out.print(sample+" ");

 Pattern pattern = Pattern.compile("\\s*([RQrq1-6])\\s*");
 Matcher matcher = pattern.matcher(sample);

 if (matcher.matches()) {
  System.out.println("match '"+matcher.group(1)+"'");
 } else {
  System.out.println("fail");
 }
}

Here are the critical lines of the output:

testIt(" 6");// ignore space tests
testIt(" 7");// ignore space tests
testIt("6 ");// ignore space tests
testIt("7 ");// ignore space tests

    6 match '6'
    7 fail
   6  match '6'
   7  fail

It uses a concept called a "capture group". A capture group is a section of a regex which is assigned a number so that the specific string of symbols that matched can be extracted. We're using a capture group here to pull the symbol out from the optional whitespace.

cartoonfox
I was just wondering, since a valid input consists of a single character, would the above spike example accept a sequence of characters? Say RR? (which by the way should fail, because it is not a vaild input)
denchr
Your example of a repeating char - "RR" is ccovered by my test case "11" above. Notice it passes on the original regex but fails to match after removing the star.
cartoonfox
Yes, i see that now. Thanks for pointing it out
denchr
Hmm.. is there a way to ignore a blank "", without giving a match, or a fail? How can the concept of ignore be implemented?
denchr
I've added a section to the bottom of my answer to do that. It slightly complicates things - because you need to use a "capture group" inside the regex to do that.
cartoonfox
So would that work for just a blank (" ") input as well?
denchr
Blank lines will still fail - because there's nothing on them that the regex can recognize. I don't know if you'd want to just prompt the user again if you didn't get something valid?
cartoonfox
Perhaps that's a way to go. My initial approach would be to simply ignore the blank (" "), meaning the system will not react, no output, or anything
denchr
A: 

If I read your question right, you want to call your program like this:

$ myprog 153r34r2q

Then, your program goes through that string of single letter commands and performs them (perhaps to something you pass in in further arguments).

For that, your regex solution is almost OK (use /[RrQq1-6]+/ instead of /[RrQq1-6]*/).

However, I suspect that you actually want to run your program, then accept user commands while the program is running (this assumption is caused by the names of your commands "replay" and "quit", which don't make much sense otherwise). For that, args[] is not suited at all. Instead, you will have to use a main loop which accepts single keystrokes as input. In this case, I wouldn't use a regular expression to test a single character, but use a switch clause for dispatching to the different commands that has a default case to catch invalid input.

Edit: After having read your comment, I propose that you might want to use something along the Command Pattern.

Svante
Actually, yes, I think you hit the nail right on the head. Thanks for helping me clarify my question. Single keystrokes should be the input: lets say I press "1" then enter - I see some output relevant to task 1, then I press "R" and I should see some output relevant to my last actual command (1) - I hope this is a bit clearer
denchr