Java: Infinite loop using Scanner in.hasNextInt() - java

I am using the following code:
while (invalidInput)
{
// ask the user to specify a number to update the times by
System.out.print("Specify an integer between 0 and 5: ");
if (in.hasNextInt())
{
// get the update value
updateValue = in.nextInt();
// check to see if it was within range
if (updateValue >= 0 && updateValue <= 5)
{
invalidInput = false;
}
else
{
System.out.println("You have not entered a number between 0 and 5. Try again.");
}
} else
{
System.out.println("You have entered an invalid input. Try again.");
}
}
However, if I enter a 'w' it will tell me "You have entered invalid input. Try Again." and then it will go into an infinite loop showing the text "Specify an integer between 0 and 5: You have entered an invalid input. Try again."
Why is this happening? Isn't the program supposed to wait for the user to input and press enter each time it reaches the statement:
if (in.hasNextInt())

In your last else block, you need to clear the 'w' or other invalid input from the Scanner. You can do this by calling next() on the Scanner and ignoring its return value to throw away that invalid input, as follows:
else
{
System.out.println("You have entered an invalid input. Try again.");
in.next();
}

The problem was that you did not advance the Scanner past the problematic input. From hasNextInt() documentation:
Returns true if the next token in this scanner's input can be interpreted as an int value in the default radix using the nextInt() method. The scanner does not advance past any input.
This is true of all hasNextXXX() methods: they return true or false, without advancing the Scanner.
Here's a snippet to illustrate the problem:
String input = "1 2 3 oops 4 5 6";
Scanner sc = new Scanner(input);
while (sc.hasNext()) {
if (sc.hasNextInt()) {
int num = sc.nextInt();
System.out.println("Got " + num);
} else {
System.out.println("int, please!");
//sc.next(); // uncomment to fix!
}
}
You will find that this program will go into an infinite loop, asking int, please! repeatedly.
If you uncomment the sc.next() statement, then it will make the Scanner go past the token that fails hasNextInt(). The program would then print:
Got 1
Got 2
Got 3
int, please!
Got 4
Got 5
Got 6
The fact that a failed hasNextXXX() check doesn't skip the input is intentional: it allows you to perform additional checks on that token if necessary. Here's an example to illustrate:
String input = " 1 true foo 2 false bar 3 ";
Scanner sc = new Scanner(input);
while (sc.hasNext()) {
if (sc.hasNextInt()) {
System.out.println("(int) " + sc.nextInt());
} else if (sc.hasNextBoolean()) {
System.out.println("(boolean) " + sc.nextBoolean());
} else {
System.out.println(sc.next());
}
}
If you run this program, it will output the following:
(int) 1
(boolean) true
foo
(int) 2
(boolean) false
bar
(int) 3

This statement by Ben S. about the non-blocking call is false:
Also, hasNextInt() does not block. It's the non-blocking check to see if a future next call could get input without blocking.
...although I do recognize that the documentation can easily be misread to give this opinion, and the name itself implies it is to be used for this purpose. The relevant quote, with emphasis added:
The next() and hasNext() methods and their primitive-type companion methods (such as nextInt() and hasNextInt()) first skip any input that matches the delimiter pattern, and then attempt to return the next token. Both hasNext and next methods may block waiting for further input. Whether a hasNext method blocks has no connection to whether or not its associated next method will block.
It is a subtle point, to be sure. Either saying "Both the hasNext and next methods", or "Both hasnext() and next()" would have implied that the companion methods would act differently. But seeing as they conform to the same naming convention (and the documentation, of course), it's reasonable to expect they act the same, and hasNext()
clearly says that it can block.
Meta note: this should probably be a comment to the incorrect post, but it seems that as a new user I can only post this answer (or edit the wiki which seems to be preferred for sytlistic changes, not those of substance).

Flag variables are too error prone to use. Use explicit loop control with comments instead. Also, hasNextInt() does not block. It's the non-blocking check to see if a future next call could get input without blocking. If you want to block, use the nextInt() method.
// Scanner that will read the integer
final Scanner in = new Scanner(System.in);
int inputInt;
do { // Loop until we have correct input
System.out.print("Specify an integer between 0 and 5: ");
try {
inputInt = in.nextInt(); // Blocks for user input
if (inputInt >= 0 && inputInt <= 5) {
break; // Got valid input, stop looping
} else {
System.out.println("You have not entered a number between 0 and 5. Try again.");
continue; // restart loop, wrong number
}
} catch (final InputMismatchException e) {
System.out.println("You have entered an invalid input. Try again.");
in.next(); // discard non-int input
continue; // restart loop, didn't get an integer input
}
} while (true);

Related

Scanner#nextLine() leaves a leftover newline character

I've been running into a problem with Scanner#nextLine. From my understanding, nextLine() should return the rest of the current input stream and then move on to the next line.
while (true){
try{
System.out.println("Please enter a month in numeric form");
month = input.nextInt();
System.out.println("Please enter a day in numeric form");
day = input.nextInt();
System.out.println("Please enter a two-digit year");
if (input.hasNextInt() == true){
year = input.next();
}
else{
throw new java.util.InputMismatchException();
}
break;
}
catch(Exception e){
System.err.println(e);
System.out.println("\nOne of your inputs was not valid.");
System.out.println(input.nextLine());
}
}
The problem is the last line. If I leave it as input.nextLine(), the next iteration of the loop accepts a newline character for the month. Why is that? Shouldn't the call to nextLine in the catch block consume the rest of the line (including the newline) and prompt the user correctly in the next iteration? Note: I've decided to print them to try and figure out what's happening, but no cigar.
I've gathered some output from the terminal to illustrate what I mean:
// What should happen (this is when catch contains input.next() rather than nextLine)
/*
Please enter a month in numeric form
8
Please enter a day in numeric form
2
Please enter a two-digit year
badinput
java.util.InputMismatchException
One of your inputs was not valid.
badinput
Please enter a month in numeric form <------------- prompts for input, as expected
*/
// What happens when I have nextLine in the catch block (code above)
/*
Please enter a month in numeric form
8
Please enter a day in numeric form
2
Please enter a two-digit year
badinput
java.util.InputMismatchException
One of your inputs was not valid.
<------------ leftover newline printed, as expected
Please enter a month in numeric form <---------------- does not prompt for input due to another leftover newline (why?)
java.util.InputMismatchException
One of your inputs was not valid.
badinput <----------------------- prints badinput even though it should've been consumed on the last iteration (why?)
Please enter a month in numeric form
*/
Before someone marks this as a duplicate, please understand that I've looked at the differences between next and nextLine on stackoverflow already. nextLine should consume the newline character but it doesn't seem to do that here. Thanks.
if (input.hasNextInt() == true) { // prefer `if(input.hasNextInt())`
year = input.next();
} else {
throw new java.util.InputMismatchException();
}
for input badinput will evaluate input.hasNextInt() as false which means that else block will be executed without consuming that badinput (to do it we need to call next() - not nextLine() because as you probably know if we use nextLine after nextInt we will consume remaining line separator, not value from next like, more info at Scanner is skipping nextLine() after using next(), nextInt() or other nextFoo() methods).
So since else block simply throws exception it moves control flow to catch section. This means we are skipping break so our loop will need to iterate once again.
Now in catch section you are simply printing
System.err.println(e);
System.out.println("\nOne of your inputs was not valid.");
System.out.println(input.nextLine());
which prints exception e, string "\nOne of your inputs was not valid." and result of nextLine() (which as explained before) will simply consume line separators which remained after last nextInt() call, so we still didn't consume badinput from Scanner.
This means that when loop starts another iteration and asks for month, it receives batinput which is not valid int so nextInt() throws InputMismatchException. And again we end up in catch block and we call nextLine() which this time consumes badinput.
Now since we finally consumed that incorrect value loop will start another iteration and we will be asked for value for month.
To avoid this kind of problems please read examples from: Validating input using java.util.Scanner. In first example you will find way to validate each input at time it is provided
Scanner sc = new Scanner(System.in);
int number;
do {
System.out.println("Please enter a positive number!");
while (!sc.hasNextInt()) {
System.out.println("That's not a number!");
sc.next(); // this is important!
}
number = sc.nextInt();
} while (number <= 0);
System.out.println("Thank you! Got " + number);
To avoid writing this code many times create your own utility method. You can even skip condition where you require from number to be positive like:
public static int getInt(Scanner sc, String askMsg) {
System.out.println(askMsg);
while (!sc.hasNextInt()) {
System.out.println("That's not a number. Please try again");
sc.next(); // consuming incorrect token
}
//here we know that next value is proper int so we can safely
//read and return it
return sc.nextInt();
}
With this method your code could be reduced to
Scanner input = new Scanner(System.in);
int month = getInt(input, "Please enter a month in numeric form");
int day = getInt(input, "Please enter a day in numeric form");
int year = getInt(input, "Please enter a two-digit year");
You can add another version of that utility method in which you can let programmer add conditions which number should pass. We can use IntPredicate functional interface for that added in Java 8, which will allow us to create conditions using lambdas like
public static int getInt(Scanner sc, String askMsg, IntPredicate predicate) {
System.out.println(askMsg);
int number;
boolean isIncorrect = true;
do {
while (!sc.hasNextInt()) {
String value = sc.next(); // consuming incorrect token
System.out.println(value + " is not valid number. Please try again");
}
number = sc.nextInt();
if (!predicate.test(number)) {
System.out.println(number + " is not valid number. Please try again");
}else{
isIncorrect=false;
}
} while (isIncorrect);
return number;
}
Usage:
int year = getInt(input, "Please enter a two-digit year", i -> (i>=10 && i<=99));
I suspect when you are entering two digit year, and as you are using next() to read it so it will read the next string only. And it will leave 2 to be read by your nextLine() new line or empty value before even you enter the value for your 2 digit year and anything after that will be left over including the new line or carriage return as you entered an invalid value. So your nextLine() inside catch just reads that left over part of invalid input but leaves the new line or carriage return as is. which is causing the exception to occur while you expect prompt to appear to read month. you can place nextLine() after each nextInt() or next() to resolve the issue.
Remember, the Scanner does not see your print statements, it just reads input as a stream of characters. The fact that you, as a user, enter those characters one line at a time is meaningless to the scanner.
So, you type 8<ENTER> (where <ENTER> represents that actual newline character(s) of your OS). After nextInt(), the 8 has been consumed.
You then type 2<ENTER>, making the pending input <ENTER>2<ENTER>. Remember, only the 8 was consumed, so far. nextInt() skips then whitespace and returns 2, thereby consuming <ENTER>2.
You then type badinput<ENTER>, making the pending input <ENTER>badinput<ENTER>. Since the next token is not a valid integer number, you throw exception, and enter the catch block, where you call nextLine(). It consumes all character up to and including the first <ENTER>, and returns the text before, i.e. an empty string.
At this point in time, badinput<ENTER> is still pending in the stream, and is processed when you loop back.
This is one of the main flaws in how people use Scanner. nextInt() doesn't consume the line, only the token, leaving the rest of the line behind.
Example of how things go bad with Scanner:
Please enter a month in numeric form
8 2 17
Please enter a day in numeric form
Please enter a two-digit year
Because user entered all 3 values on the first line, you code will get the values, but will still print the next two prompts, even though that is unnecessary. It's just weird like that.
Solution 1: Don't use Scanner. It's just too dang weird. Too easy to use, and soooo easy to misuse, aka soooo difficult to use correctly.
Solution 2: Call nextLine() after each nextInt() to flush (silently consume) any extra text after the accepted value. If you do that, the example would go like this:
Please enter a month in numeric form
8 2 17
Please enter a day in numeric form
2
Please enter a two-digit year
17
The <SPACE>2<SPACE>17<ENTER> on the first line would be silently ignored.
You could extend the logic to if (! nextLine().trim().isEmpty()) {/*ERROR*/} if you want full error handling.

hasNextInt() and nextInt() method flow

How does this conditional statement (from Udacity's Intro to Java Programming | Problem Set 4 | Question #20) work?
import java.util.Scanner;
public class MonthPrinter {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("Enter a month number (1 through 12) ");
if (!in.hasNextInt()) {
System.out.println("Not an integer. Terminating");
} else {
int theMonthNumber = in.nextInt();
if (!(theMonthNumber >= 1 && theMonthNumber <= 12)) {
System.out.println("Number must be 1 through 12");
} else {
Month test = new Month(theMonthNumber);
System.out.print(test.getMonthName() + " " + test.getNumberOfDays());
}
}
}
}
The first if (!in.hasNextInt()) checks to see if the user input is an integer. If it's NOT an integer, the main method prints Not an integer. Terminating. That makes complete sense.
However, in the event that the user inputs an integer, the code proceeds to the else statement where the next line of code is int theMonthNumber = in.nextInt();
When the program runs and I provide an integer as an input, I'm NOT prompted for another input. I'm thinking that both the hasNextInt() method and nextInt() method should request an input from the user. Therefore, I should be prompted for a total of two inputs (assuming I provide an integer). When I dry-run this scenario, I input an integer 3. This passes the if(!hasNextInt()) check.
What am I missing in the logical flow of the statement(s)?
I'm thinking in my mind that both the hasNextInt() method and nextInt() method should request an input from the user.
No. The Scanner.hasNextInt() Javadoc says (in part)
Returns true if the next token in this scanner's input can be interpreted as an int value in the default radix using the nextInt() method. The scanner does not advance past any input.
The last sentence is telling you that it does not consume the int.
Function hasNextInt() is just for checking the input value. It will not consume the input. What happens is, it will advance the Scanner and check the data but after that it will go back to it's previous position. So technically, the position of Scanner has not changed. That's why it's not prompting for next input.
Only nextInt() will consume the data. Scanner will advance to the next position after calling this function.

How does writing a new scanner in a while loop stop an otherwise infinite loop?

So I am trying to stop an error from happening if the user inputs a number other than 1,2,3, so I created a while loop to make the program keep asking the user for a new number if their input is outside 1,2,3.
Before I didn't have the
Scanner user1 = new Scanner(System.in);
input = user1.nextInt();
lines, and the while loop kept running infinitely. But now the code does what I want, it stops and evaluates the new user input for as many times as the input is wrong (I've entered 3,6,7 wrong values before a correct one and it works every time).
My question is, how does the scanner stop the infinite loop? Does the Scanner impletmentation cause the computer to wait for a user input before it continues, and so because of this it goes back to evaluate it continuously, instead of infinitely printing out "I'm sorry that's not a valid input..." ? I just want to be sure I know WHY it's stopping.
Scanner user = new Scanner(System.in);
System.out.println("Hello, what would you like to do?" + "\n" + "1. Search" + "\n" + "2. Add new instructor" + "\n" + "3. Remove Intsructor");
int input = user.nextInt();
boolean valid = false;
while(valid == false)
{
if(input<3 && input>=1)
{
valid = true;
}
else
{
System.out.println("I'm sorry, that's not a valid input, please enter 1, 2, or 3.");
Scanner user1 = new Scanner(System.in);
input = user1.nextInt();
}
It stops the loop by waiting. nextInt() is a blocking method, meaning when you call it, the thread that called it will wait until that method either returns a value, or the reason for the block has finished.
When you call nextInt(), your thread haults, and waits until something comes through System.in. Once something comes in, the method returns the value.
If you put scanner.nextInt() inside of of your loop, then each loop iteration will wait for user input
Scanner scanner = new Scanner(System.in);
boolean valid = true;
while(valid) {
switch(scanner.nextInt()) {
case 1:
case 2:
valid = false; //if nextInt() is 1 or 2
break;
default: //anything else
break;
}
}
Consider this:
Your loop stops only when valid is set to true
valid is set to true only when input is 1 or 2
If input is outside the valid range, valid does not change
If input is outside the range upon the first entry into the loop, the only way to stop the loop is to change the value of input.
Without the two lines that you added, i.e. without
Scanner user1 = new Scanner(System.in);
input = user1.nextInt();
the loop does not change input. Therefore, if the loop is not exited right away, it stays infinite.
Note that you do not need to create a new scanner - you can read from the one that you created before the loop, i.e.
input = user.nextInt();
Also note that calling nextInt() without calling hasNextInt() may produce an exception.

try/catch infinite loop? [duplicate]

This question already has answers here:
How to handle infinite loop caused by invalid input (InputMismatchException) using Scanner
(5 answers)
Closed 5 years ago.
Help, I am completely new to java and I am trying to create a loop that will ask for an input from the user which is to be a number. If the user enters anything other than a number I want to catch the exception and try again to get the correct input. I did this with a while loop however it does not give the opportunity after the error for the user to type in anything it loops everything else but that. Please help me to see understand what is wrong and the correct way to do this... Thank you. This is what I have:
import java.util.Scanner;
import java.util.InputMismatchException;
public class simpleExpressions {
public static void main (String[] args) {
Scanner keyboard = new Scanner(System.in);
while ( true ) {
double numOne;
System.out.println("Enter an Expression ");
try {
numOne = keyboard.nextInt();
break;
} catch (Exception E) {
System.out.println("Please input a number only!");
} //end catch
} //end while
} //end main
while ( true )
{
double numOne;
System.out.println("Enter an Expression ");
try {
numOne = keyboard.nextInt();
break;
}
catch (Exception E) {
System.out.println("Please input a number only!");
}
This suffers from several problems:
numOne hasn't been initialized in advance, so it will not be definitely assigned after the try-catch, so you won't be able to refer to it;
if you plan to use numOne after the loop, then you must declare it outside the loop's scope;
(your immediate problem) after an exception you don't call scanner.next() therefore you never consume the invalid token which didn't parse into an int. This makes your code enter an infinite loop upon first encountering invalid input.
Use keyboard.next(); or keyboard.nextLine() in the catch clause to consume invalid token that was left from nextInt.
When InputMismatchException is thrown Scanner is not moving to next token. Instead it gives us opportunity to handle that token using different, more appropriate method like: nextLong(), nextDouble(), nextBoolean().
But if you want to move to other token you need to let scanner read it without problems. To do so use method which can accept any data, like next() or nextLine(). Without it invalid token will not be consumed and in another iteration nextInt() will again try to handle same data throwing again InputMismatchException, causing the infinite loop.
See #MarkoTopolnik answer for details about other problems in your code.
You probably want to use a do...while loop in this case, because you always want to execute the code in the loop at least once.
int numOne;
boolean inputInvalid = true;
do {
System.out.println("Enter an expression.");
try {
numOne = keyboard.nextInt();
inputInvalid = false;
} catch (InputMismatchException ime) {
System.out.println("Please input a number only!");
keyboard.next(); // consume invalid token
}
} while(inputInvalid);
System.out.println("Number entered is " + numOne);
If an exception is thrown then the value of inputInvalid remains true and the loop keeps going around. If an exception is not thrown then inputInvalid becomes false and execution is allowed to leave the loop.
(Added a call to the Scanner next() method to consume the invalid token, based on the advice provided by other answers here.)

Java .nextLine() repeats line

Everything of my guessing game is alright, but when it gets to the part of asking the user if he/she wants to play again, it repeats the question twice. However I found out that if I change the input method from nextLine() to next(), it doesn't repeat the question. Why is that?
Here is the input and output:
I'm guessing a number between 1-10
What is your guess? 5
You were wrong. It was 3
Do you want to play again? (Y/N) Do you want to play again? (Y/N) n
Here is the code:(It is in Java)
The last do while loop block is the part where it asks the user if he/she wants to play again.
import java.util.Scanner;
public class GuessingGame
{
public static void main(String[] args)
{
Scanner input = new Scanner(System.in);
boolean keepPlaying = true;
System.out.println("Welcome to the Guessing Game!");
while (keepPlaying) {
boolean validInput = true;
int guess, number;
String answer;
number = (int) (Math.random() * 10) + 1;
System.out.println("I'm guessing a number between 1-10");
System.out.print("What is your guess? ");
do {
validInput = true;
guess = input.nextInt();
if (guess < 1 || guess > 10) {
validInput = false;
System.out.print("That is not a valid input, " +
"guess again: ");
}
} while(!validInput);
if (guess == number)
System.out.println("You guessed correct!");
if (guess != number)
System.out.println("You were wrong. It was " + number);
do {
validInput = true;
System.out.print("Do you want to play again? (Y/N) ");
answer = input.nextLine();
if (answer.equalsIgnoreCase("y"))
keepPlaying = true;
else if (answer.equalsIgnoreCase("n"))
keepPlaying = false;
else
validInput = false;
} while (!validInput);
}
}
}
In your do while loop, you don't want the nextLine(), you just want next().
So change this:
answer = input.nextLine();
to this:
answer = input.next();
Note, as others have suggested, you could convert this to a while loop. The reason for this is that do while loops are used when you need to execute a loop at least once, but you don't know how often you need to execute it. Whilst it's certainly doable in this case, something like this would suffice:
System.out.println("Do you want to play again? (Y/N) ");
answer = input.next();
while (!answer.equalsIgnoreCase("y") && !answer.equalsIgnoreCase("n")) {
System.out.println("That is not valid input. Please enter again");
answer = input.next();
}
if (answer.equalsIgnoreCase("n"))
keepPlaying = false;
The while loop keeps looping as long as "y" or "n" (ignoring case) isn't entered. As soon as it is, the loop ends. The if conditional changes the keepPlaying value if necessary, otherwise nothing happens and your outer while loop executes again (thus restarting the program).
Edit: This explains WHY your original code didn't work
I should add, the reason your original statement didn't work was because of your first do while loop. In it, you use:
guess = input.nextInt();
This reads the number off the line, but not the return of the line, meaning when you use:
answer = input.nextLine();
It immediately detects the leftover carriage from the nextInt() statement. If you don't want to use my solution of reading just next() you could swallow that leftover by doing this:
guess = input.nextInt();
input.nextLine();
rest of code as normal...
The problem really lies in a completely different segment of code. When in the previous loop guess = input.nextInt(); is executed, it leaves a newline in the input. Then, when answer = input.nextLine(); is executed in the second loop, there already is a newline waiting to be read and it returns an empty String, which activates the final else and validInput = false; is executed, to repeat the loop (and the question).
One solution is to add an input.nextLine(); before the second loop. Another is to read guess with nextLine() and then parse it into an int. But this complicates things as the input could not be a correct int. On a second thought, the code already presents this issue. Try entering a non-numeric response. So, define a function
public static int safeParseInt(String str) {
int result;
try {
result= Integer.parseInt(str) ;
} catch(NumberFormatException ex) {
result= -1 ;
}
return result ;
}
And then replace your first loop with:
do {
validInput= true ;
int guess= safeParseInt( input.nextLine() ) ;
if( guess < 1 || guess > 10 ) {
validInput= false ;
System.out.print("That is not a valid input, guess again: ");
}
} while( !validInput );
PS: I don't see any problem with do-while loops. They are part of the language, and the syntax clearly indicates that the condition is evaluated after the body is executed at least one time. We don't need to remove useful parts of the language (at least from practice) just because others could not know them. On the contrary: if we do use them, they will get better known!
validInput = false;
do {
System.out.print("Do you want to play again? (Y/N) ");
answer = input.next();
if(answer.equalsIgnoreCase("y")){
keepPlaying = true;
validInput = true;
} else if(answer.equalsIgnoreCase("n")) {
keepPlaying = false;
validInput = true;
}
} while(!validInput);
I changed the coding style as I find this way more readable.
Your problem is that nextInt will stop as soon as the int ends, but leaves the newline in the input buffer. To make your code correctly read the answer, you'd have to enter it on the same line as your guess, like 5SpaceYReturn.
To make it behave more than one would expect, ignore the first nextLine result if it contains only whitespace, and just call nextLine again in that case without printing a message.
I believe the output of input.nextLine() will include the newline character at the end of the line, whereas input.next() will not (but the Scanner will stay on the same line). This means the output is never equal to "y" or "n". Try trimming the result:
answer = input.nextLine().trim();

Categories

Resources