Chairperson and Program Coordinator for the Computer Science Technology Program at Dawson College Instructor and Program Consultant for the School of Extended Learning Computer Institute at Concordia University I have been passionate about programming since buying an Apple][+ in 1980. I paid the extra $450 to bring the RAM up to 48K! Ken has posted 15 posts at DZone. You can read more from them at their website. View Full User Profile

NetBeans in the Classroom: Scanners, Regular Expressions, and Beethoven

06.15.2014
| 2092 views |
  • submit to reddit
Ken Fogel is the Program Coordinator and Chairperson of the Computer Science Technology program at Dawson College in Montreal, Canada. He is also a Program Consultant to and part-time instructor in the Computer Institute of Concordia University's School of Extended Learning. He blogs at omniprogrammer.com and tweets @omniprof. His regular columns about NetBeans in education are listed here.
 

In the courses that I teach, I explain the importance of validation of user input. Validation should occur as close to the end user as possible and as soon as possible. Before data finds its way to its destination in an object, it should be subject to validation. This should happen immediately after the input is complete and before any subsequent input. In some cases it is even possible to validate input keystroke by keystroke. I tell my students to use every means available.

In a previous article, I wrote about using a simple regular expression to validate character input. The expression ensured that the range of allowable characters was determined by the expression. I have always been in awe of programmers who can write regular expressions. Since it is not part of the curriculum that I teach I have avoided learning this craft. I can get away with this because there are so many great web sites that will write the expression for me. A few years ago I found the Utility Mill and their Regex_For_Range at this location:

http://utilitymill.com/utility/Regex_For_Range

This web based utility will accept a minimum and maximum value for a range and then generates the regular expression. The only restriction is that it is a range of positive integers starting at zero. For example, I was looking to validate the range from 0-744. Entering this information into the web page’s form gave me:

([0-9]{1,2}|[1-6][0-9]{2}|7[0-3][0-9]|74[0-4])

I won’t bother to explain how this works because one of the options on the form is ‘Verbose (See how the pattern is built step by step)’ where the expression is clearly explained. I strongly recommend using this option. It makes me think I could write a regular expression on my own, maybe.

Now I can write a Scanner input routine that looks like this:

int number = 0;
System.out.println("Enter a number from 0 to 744: ");
if (sc.hasNext("([0-9]|[1-9][0-9]|[1-7][0-4][0-4])")) {
   number = sc.nextInt();
} else {
   number = -1;
}
Notice that the regular expression is used in hasNext() and not hasNextInt(). Only the String version of the hasXXX() methods can accept a regular expression. If the pattern of the string matches the expression the it is safe to read it with the nextInt() method. If the match fails then a value outside the range such as -1 is assigned to the number.

I implement this using a pattern that is probably quite old and definitely inefficient. The pattern is the Primary Read, Secondary Read. I have actually never found a web page or reference to this. I learned it from a COBOL teacher in the 80s.

In this pattern you first ask the user for input and immediately validate it. If the validation fails you enter a loop where you are asked again for the input but with a message that indicates that something just went wrong. You stay in this loop until you enter valid input.

    /**
     * Primary Read, Secondary Read Pattern
     *
     * @return
     */
    public int getANumber() {
        int number;
        // Primary Read
        System.out.println("Enter a number from 0 to 744: ");
        if (sc.hasNext("([0-9]|[1-9][0-9]|[1-7][0-4][0-4])")) {
            number = sc.nextInt();
        } else {
            number = -1;
        }
        sc.nextLine(); // clear out the input buffer

        // Secondary Read
        while (number == -1) {
            System.out.println("Invalid. Enter a number from 0 to 744: ");
            if (sc.hasNext("([0-9]|[1-9][0-9]|[1-7][0-4][0-4])")) {
                number = sc.nextInt();
            } else {
                number = -1;
            }
            sc.nextLine();  // clear out the input buffer
        }
        return number;
    }

Normally all local variables such as number in this example should be initialized. However the first if statement ensures that number receives a value.

User input terminates when the first whitespace character is encountered. These characters are the space, the tab and the newline. If a use entered “123 210” then 123 would be accepted but 210 would remain in the input buffer so that it will be accepted as the next user input. The sc.nextLine() reads anything left in the buffer to guard against this.

The problem with this code is that the majority of the code is repeated. It becomes easy to make a change in one hasNext() and forget to make the change in the other one. The solution is to combine them into one loop. Here now is the finished version.

    /**
     * Validated input
     * @return 
     */
    public int getANumber2() {
        int number;
        do {
            System.out.println("Enter a number from 0 to 744: ");
            if (sc.hasNext("([0-9]|[1-9][0-9]|[1-7][0-4][0-4])")) {
                number = sc.nextInt();
            } else {
                number = -1;
            }
            sc.nextLine();  // clear out the keyboard buffer
            if (number == -1) {
                System.out.print("Your input was invalid. ");
            }
        } while (number == -1);
        return number;
    }


The Beethoven Test


The Beethoven Test is a tongue-in-cheek technique for determining if an input routine is user-proof. It is very simple to carry out. Run your program and at every input hum the opening notes of Beethoven’s Fifth Symphony.



While humming pretend to play these notes using as many keys on the keyboard as possible. If you are an Iron Butterfly fan you can use In-A-Gadda-Da-Vida. After doing this a few times press the enter key.

If your program does not crash but does reject the input then it has passed the test. Any other result indicates a failure with your input routine.

Published at DZone with permission of its author, Ken Fogel.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Rudolf Pecinovský replied on Sat, 2014/06/21 - 2:24pm

 Wrong regular expression. The right one is:

"([0-9]|[1-9][0-9]|[1-6][0-9][0-9]|(7([0-3][0-9]|4[0-4])))"

Ken Fogel replied on Mon, 2014/06/23 - 8:45am

Thank you for pointing out the error. The expression from the UtilityMill as well as what you suggest work perfectly. Unfortunately and to my embarrassment the sample code uses an incorrect expression. Here is what the code should look like using the UtilityMill expression.

/**
 * Primary Read, Secondary Read Pattern - Corrected
 *
 * @return
 */
public int getANumber() {
    int number;
    // Primary Read
    System.out.println("Enter a number from 0 to 744: ");
    if (sc.hasNext("([0-9]{1,2}|[1-6][0-9]{2}|7[0-3][0-9]|74[0-4])")) {
        number = sc.nextInt();
    } else {
        number = -1;
    }
    sc.nextLine(); // clear out the input buffer

    // Secondary Read
    while (number == -1) {
        System.out.println("Invalid. Enter a number from 0 to 744: ");
        if (sc.hasNext("([0-9]{1,2}|[1-6][0-9]{2}|7[0-3][0-9]|74[0-4])")) {
            number = sc.nextInt();
        } else {
            number = -1;
        }
        sc.nextLine();  // clear out the input buffer
    }
    return number;
}
/**
 * Validated input - Corrected
 * @return 
 */
public int getANumber2() {
    int number;
    do {
        System.out.println("Enter a number from 0 to 744: ");
        if (sc.hasNext("([0-9]{1,2}|[1-6][0-9]{2}|7[0-3][0-9]|74[0-4])")) {
number = sc.nextInt(); } else { number = -1; } sc.nextLine(); // clear out the keyboard buffer if (number == -1) { System.out.print("Your input was invalid. "); } } while (number == -1); return number; }

Ken Fogel replied on Mon, 2014/06/23 - 8:57am in response to: Rudolf Pecinovský

Regular expressions could certainly use a champion who can explain how they are created. You should write about it here.


Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.