Reputation: 1215
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
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
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
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
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
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
Reputation: 57
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
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