Fab
Fab

Reputation: 1215

JAVA - SCANNER to get user input

I have the following block code:

    boolean run = true;
    while (run) {
        Scanner scanner = new Scanner(System.in);
        // do something

        System.out.println("Another action? [Y/N]");
        while (!scanner.hasNext("[YyNn]")) {
            System.out.println("Incorrect input");
            scanner.next();
        }
        String choice = scanner.next();
        if (choice.toLowerCase().equals("f"))
            run = false;

        scanner.close();
    }

I want to perform something until the user types 'N'. Currently, I'm able to run the while loop only one time. The second time I'm not able to type my choice and I have the following error:

Another action? [Y/N]     <--- here no chance to type choice
Incorrect input
java.util.NoSuchElementException
    at java.util.Scanner.throwFor
    at java.util.Scanner.next

What is wrong? Thanks

EDIT: I think my problem is in "do something". It is in turn a sequence of user input where i close the scanner.

Upvotes: 0

Views: 2015

Answers (7)

LuisFerrolho
LuisFerrolho

Reputation: 417

The problem in fact is in your while loop condition. Let's debug:

When you only input chars that do not match with the pattern [YyNn] all goes ok (user will always get the Incorrect input message). But when you give, let's say an Y, your while loop will stop but Y will not be consumed. Therefore, this char will be consumed in the next instruction, i.e. in String choice = scanner.next(); (if you print variable choice you will see it has an Y).

After this, we go to the next iteration. Because there are no input to be consumed, scanner.hasNext("[YyNn]") will be false. But your while loop condition is !scanner.hasNext("[YyNn]"), which give us a true and enters inside the loop. Inside the loop we have the instruction scanner.next(). Since there are no input to be consumed, BOOM, you get a java.util.NoSuchElementException, which says the following:

Thrown by the nextElement method of an Enumeration to indicate that there are no more elements in the enumeration.

Other of your problems is in your scanner position. At each iteration you are initializing a new instance and closing it. You could just move the scanner initialization and its closure outside the loops.

Below I provide a similar sample code that does what you want with some explanations.

FULL CODE

public static void main(String[] args) {
    boolean run = true;
    Scanner scanner = new Scanner(System.in); // Init before loops
    String userInput = ""; // tmp var that holds input

    while (run) {
        // do something

        System.out.println("Another action? [Y/N]");
        userInput = scanner.next(); // Read first time

        // Run while user does not put Y || y || N || n
        while (!userInput.matches("[YyNn]")){
            System.out.println("Incorrect input");
            userInput = scanner.next();
        }

        // User does not want more actions
        if(userInput.matches("[Nn]")){
            System.out.println("Do you wish to exit the program? [Y/Any other key]");
            String choice = scanner.next();

            // Stop the program
            if (choice.toLowerCase().equals("y"))
                run = false;
        }
    }
    scanner.close(); // Close scanner outside
}

Hope it helped!

Upvotes: 1

cse
cse

Reputation: 4104

As many of the answers suggests, the problem is with the statement Scanner scanner = new Scanner(System.in);. Following is the corrected version using try-with-resources, a cleaner way to handle resources in Java:

 boolean run = true;
 try(Scanner scanner = new Scanner(System.in))
    {
        while (run) 
        {
            System.out.println("Another action? [Y/N]");
            while (!scanner.hasNext("[YyNnFf]")) {
                System.out.println("Incorrect input");
                        scanner.next();
            }
            String choice = scanner.next();
            if (choice.toLowerCase().equalsIgnoreCase("f"))
                        run = false;
        }
    }

You can see it working here.

NOTE: I have changed the pattern matching statement from [YyNn] to [YyNnFf] and choice.toLowerCase().equals("f") to choice.toLowerCase().equalsIgnoreCase("f"), because It seems a logical error. See if it is as per your need.

Upvotes: 1

Przemysław Moskal
Przemysław Moskal

Reputation: 3609

First thing: You need to move both scanner initialization line and its close() method outside the loop.

Second thing: In the checking condition add fF to let your program exit loop if one of these letters are typed while (!scanner.hasNext("[YyNnFf]")).

public static void main(String[] args) {
    boolean run = true;
    Scanner scanner = new Scanner(System.in);
    while (run) {
        // do something
        System.out.println("Another action? [Y/N]");
        while (!scanner.hasNext("[YyNnFf]")) {
            System.out.println("Incorrect input");
            scanner.next();
        }
        String choice = scanner.next();
        if (choice.toLowerCase().equals("f")) {
            run = false;
        }
    }
    scanner.close();
}

Upvotes: 1

thedevd
thedevd

Reputation: 683

This is the correct version of the same using Java8

import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ScannerExample {

public static void main(String[] args) {
    boolean run = true;
    try (Scanner sc = new Scanner(System.in)) {
        while (run) {

            System.out.println("Another action? [Y/N]");
            String option = sc.next();
            Pattern patter = Pattern.compile("[YyNn]");
            Matcher m = patter.matcher(option);
            while (!m.matches()) {
                System.out.println("Incorrect input");
                option = sc.next();
                m = patter.matcher(option);
            }
            option = sc.next();
            if (option.toLowerCase().equals("f")) {
                run = false;
            }

        }
    } catch (Exception e) {
        // TODO: handle exception
    }
}

}

output:

Another action? [Y/N]
Y
N
Another action? [Y/N]
Y
N
Another action? [Y/N]
f
Incorrect input
Y
N
Another action? [Y/N]
f
Incorrect input
Y
f

Upvotes: 1

Vishnu T S
Vishnu T S

Reputation: 3914

If you call the scanner.close() method ,System.in will be closed. So Use this. Close the scanner outside the loop.

public static void main(String[] args) {
        boolean run = true;
        Scanner scanner = new Scanner(System.in);;
        while(run){
                    // do something

                    System.out.println("Another action? [Y/N]");
                    while (!scanner.hasNext("[YyNn]")) {
                        System.out.println("Incorrect input");
                        scanner.next();
                    }
                    String choice = scanner.next();
                    if(choice.toLowerCase().equals("f"))
                        run = false;

    //             

        }
          scanner.close();
    }

Output

Another action? [Y/N]
Y
Another action? [Y/N]
Y
Another action? [Y/N]
N
Another action? [Y/N]
N
Another action? [Y/N]
test
Incorrect input

Upvotes: 1

I think you should change some lines of code like this

        Scanner scanner = new Scanner(System.in);

        char choice = 'Y'

        while(choice!='n'){

          // do something

          System.out.print("Another action? [Y/N]");

          choice = scanner.nextLine().toLowerCase().charAt(0);

          if(choice!='y' || choice!='n'){

              continue;

          }

        }

        scanner.close();

Upvotes: 1

Mustapha Belmokhtar
Mustapha Belmokhtar

Reputation: 1219

I took off the scanner.close(); and added "fF" to the pattern, it runs until I input f or F:

 boolean run = true;
    while(run){
      System.out.println("Another action? [Y/N]");
        while (!scanner.hasNext("[YyNnfF]")) {
          System.out.println("Incorrehct input");
           scanner.next();
      }
        String choice = scanner.next();
         if(choice.toLowerCase().equals("f"))
            run = false;

      //  scanner.close();
    }

Upvotes: 2

Related Questions