javavalidationexceptionwhile-loop

How to properly use a try/catch block in a while loop to handle invalid input in Java?


I’m trying to write a Java program that asks the user to input a positive integer inside a while loop. The loop should continue until valid input is provided.

I’ve tried using a try/catch block to handle invalid inputs like strings, but I’m still running into issues. For example:

Here’s the code I’ve written so far:

import java.util.Scanner;

public class InputExample {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int userInput = -1;

        while (userInput < 0) {
            try {
                System.out.print("Enter a positive number: ");
                userInput = scanner.nextInt(); // Crashes if invalid input is entered
            } catch (InputMismatchException e) {
                System.out.println("Invalid input. Please enter a valid number.");
                scanner.nextLine(); // Attempt to clear the buffer, but it doesn't work
            }
        }

        System.out.println("You entered: " + userInput);
        scanner.close();
    }
}

The issue:

  1. When invalid input (like a string) is entered, the program skips re-prompting the user.

  2. I suspect there’s an issue with how I’m clearing the input buffer using scanner.nextLine().

What I’ve tried:

  1. Wrapping the scanner.nextInt() inside a try/catch block to catch the exception.

  2. Using scanner.nextLine() after catching the exception to clear the input.

My question:

How can I properly handle invalid input in this scenario so that the program keeps re-prompting the user until a valid positive integer is provided?


Solution

  • Just as a suggestion, just use the Scanner#nextLine() method and compare the User's input with the String#matches() method and a small Regular Expression, for example:

    import java.util.Scanner;
    
    public class ScannerPromptDemo {
    
        /* Don't close `System.in` unless you know it will never be
           needed again during this application session otherwise
           you will need to restart your application in order to 
           use it again. The JVM will automatically close this 
           stream and free resources when the application closes.  */
        private final Scanner userInput = new Scanner(System.in);
        private final String ls = System.lineSeparator();
        
        
        public static void main(String[] args) {
            /* App started this way to avoid the need for 
               statics unless we really want them:     */
            new ScannerPromptDemo().startDemo(args);
        }
        
        private void startDemo(String[] args) {
            // Outer loop to keep inner prompt loop cycling until "q" is entered:
            String num = "";
            while (!num.equalsIgnoreCase("q")) {
                while (num.isEmpty()) {
                    // Prompt:
                    System.out.println("Please enter any legal int, long, float, or double type number:"); 
                    System.out.print("Your number (q to quit): -> ");
                    num = userInput.nextLine().trim();
                    // Is `q` for quit provided?
                    if (num.equalsIgnoreCase("q")) {
                        //Yes...
                        System.out.println("quiting - Bye Bye" + ls);
                        break;  // Exit inner `while` loop and effectively, the outer loop as well:
                    }
    
                    /* Validate User input. Is it a string representation of 
                       a signed or unsigned Integer or Floating-Point value: */
                    if (!num.matches("-?\\d+(\\.\\d+)?")) {
                        // No... inform User and allow to try again:
                        System.out.println("Invalid number supplied (" + num + ")! Try again..." + ls);
                        num = "";  // Empty `num` to ensure re-loop:
                    }
                }
                
                if (num.equalsIgnoreCase("q")) {
                    break;
                }
    
                // Validation Passed:
                Double doubleValue = null;
                Integer intValue = null;
                Long longValue = null;
    
                if (num.contains(".")) {
                    doubleValue = Double.valueOf(num);
                }
                else {
                    longValue = Long.valueOf(num);
                    if (longValue <= Integer.MAX_VALUE) {
                        intValue = Integer.valueOf(num);
                        longValue = null;
                    }
                }
                System.out.println("You entered a "
                        + (doubleValue != null ? "`double` type value of: " + doubleValue
                                : (longValue != null ? "`long` type value of: " + longValue
                                        : "`int` type value of: " + intValue)));
                num = "";
                System.out.println();
            }
        }
    }
    

    In the example above, the User can enter any number as long as it's a legal signed or unsigned int, long, float, or double type number. You could take this a little further and add BigInteger and BigDecimal to the mix.

    The Regular Expression ("-?\\d+(\\.\\d+)?") supplied to the String#matches() method checks to see if the User supplied numerical value is in fact a signed or unsigned integer or floating-point value.

    Checks for data type are done after the inner while loop and nested Ternary Operators are used to display the result within the Console Window.