java hasNext method for checking scanner for more tokens - java

I'd like to use the snippet of code below to allow a user to enter four values at once separated by spaces, in which case they would be captured separately and assigned to variables. Alternatively, the user could enter one value at a time while waiting for prompts between each value. If the if(in.hasNext()) worked like I had hoped, this solution would have been easily doable with the code below, but I have learned that hasNext is apparently always evaluating to TRUE, even when there is no additional token in the stream, so the program blocks and waits for input. Is there a way to adjust this code to get the desired results? (month, day, and year are strings that get parsed to integers later in the program.) Thanks
Scanner in = new Scanner(System.in);
System.out.println("Please enter your first name, last name, or full name:");
traveler = in.nextLine();
System.out.println("Please enter your weight, birth month, birth day of month, and birth year as numbers.\nYou may enter all the numbers at once -- right now -- or you may enter them one at a time as the prompts appear.\nOr, you may also enter them in any combination at any time, so long as weight (at least) is entered now and the remaining \nthree numbers are eventually entered in their correct order:");
pounds = in.nextInt();
if (in.hasNext()) {
month = in.next();
} else {
System.out.println("Please enter your birth month, or a combination of remaining data as described previously, so long as the data you enter now begins with birth month:");
month = in.next();
}
if (in.hasNext()) {
day = in.next();
} else {
System.out.println("Please enter your birth day of month, or a combination of remaining data as described previously, so long as the data you enter now begins with birth day of month:");
day = in.next();
}
if (in.hasNext()) {
year = in.next();
} else {
System.out.println("If you've made it this far, then please just enter your birth year:");
year = in.next();
}

Read a line at a time, then call split(" ") to determine how many and which values the user entered.

There is a InputStream.available() method that returns how many characters can be read without blocking; however, that does not determine whether there is a complete number or line. The Scanner class's documentation explicitly says that hasNext can block, though.

Related

I'm trying to call a method through a for loop, and the first iteration will only read the first line of code in the method

I'm trying to use an enhanced for loop to call a method many times in a row, but if I'm iterating more than once, the first iteration will only read the first line of code. Here are both of the methods I'm using:
public Account() {
this.subDetails = new HashMap<Integer, String>();
System.out.println("What is the account type? (Individual/Family)");
String planType = keyboard.nextLine();
System.out.println("Enter your card number: ");
this.cardNum = keyboard.nextLine();
System.out.println("Enter the expiration date: ");
this.cardExp = keyboard.nextLine();
if (planType.equals("Family")) {
System.out.println("How many users?");
int numUsers = keyboard.nextInt();
for (int z=0; z<numUsers; z++) {
this.addUser();
}
StreamingService.numFamUsers = StreamingService.numFamUsers + numUsers;
StreamingService.monthlyRevenue = StreamingService.monthlyRevenue + 14.99;
StreamingService.monthlyFamRevenue = StreamingService.monthlyFamRevenue + 14.99;
}
else if (planType.equals("Individual")) {
this.addUser();
StreamingService.numIndUsers++;
StreamingService.monthlyRevenue = StreamingService.monthlyRevenue + 9.99;
StreamingService.monthlyIndRevenue = StreamingService.monthlyIndRevenue + 9.99;
}
StreamingService.numAccounts++;
this.subDetails.put(i, planType);
i++;
}
public void addUser() {
System.out.println("What is the email of the next user?");
String e = keyboard.nextLine();
User y = new User(e, i);
StreamingService.userEmails.add(y);
StreamingService.numUsers++;
this.acctUsers.add(e);
}
And here is the output (Data quality is poor, just used as an example):
What is the account type? (Individual/Family)
Individual
Enter your card number:
1234123412341234
Enter the expiration date:
02/24
What is the email of the next user?
abc#def.com
What is the account type? (Individual/Family)
Family
Enter your card number:
1234123412341234
Enter the expiration date:
02/24
How many users?
3
What is the email of the next user? What is the email of the next
user?
abc#def.com
What is the email of the next user?
abc#defg.com
What is the account type? (Individual/Family)
Family
Enter your card number:
1234123412341234
Enter the expiration date:
02/24
How many users?
2
What is the email of the next user? What is the email of the next
user?
xyz#abc.com
Does anyone know how to fix this?
Scanner.nextInt does NOT consume the newline-charakter you enter after the nubmers. Because of this, any readline afterwards will consume the newline - and stop, becasue that's what it does.
Because of this, the first email prompt after entering the number of users will always return an empty string. You can see this in your output: it's the cases where the email prompt is duplicated.
To fix this, put a call to keyboard.nextLine() after all of your keyboard.nextInt() calls (and just ignore the output of that function).

How to have the program wait for input before proceding

I am having a problem where my program is scanning for two different inputs at the same time.
import java.util.Scanner;
public class Person {
public static void main(String[] args){
Person p1 = new Person();
System.out.println("1: Add Person");
System.out.println("2: Delete Person");
System.out.println();
System.out.print("Please make a selection: ");
Scanner keyboard = new Scanner(System.in);
int selection = keyboard.nextInt();
switch(selection){
case 1:
System.out.print("Please enter name: ");
String name = keyboard.nextLine();
p1.addPerson(name);
break;
}
}
public Person(){
}
public void addPerson(String name){
int day, month, year;
Scanner keyboard = new Scanner(System.in);
System.out.print("Please enter date of birth in the format dd mm yyyy: ");
day = keyboard.nextInt();
month = keyboard.nextInt();
year = keyboard.nextInt();
}
}
This is the output:
1: Add Person
2: Delete Person
Please make a selection: 1
Please enter name: Please enter date of birth in the format dd mm yyyy:
The program does not wait for the name to be entered, how do i fix this?
The problem is when you do nextInt() it scans an integer, but not the new line character(\n), so when you call nextLine() it just consumes \n you typed when was doing selection and returns empty string.
Several ways to fix it:
First is to call nextLine() after nextInt. To fix your code you would do this:
int selection = keyboard.nextInt();
keyboard.nextLine();
Second option is to call nextLine() but when you need int wrap in in Integer.parseInt(). So, for example, your selection would look like:
int selection = Integer.parseInt(keyboard.nextLine());
Other option would be to use next() instead of nextLine, however, this approach wouldn't work if name contains spaces.
You should use keyboard.next()
From the java docs:
next(): Finds and returns the next complete token from this scanner.
This is what keyboard.nextLine() does:
nextLine(): Advances this scanner past the current line and returns the input that was skipped.

Java: Try to Understand Getting Inputs from Users Using Scanner

I have this programs and a few questions regarding to how .next(), .nextInt(), .hasNext() and .hasNextInt() of Scanner class work. Thank you in advance for any of your help :)
import java.util.Scanner;
public class test {
public static void main (String[] args) {
Scanner console = new Scanner(System.in);
int age;
System.out.print("Please enter your age: ");
while (!console.hasNextInt()) {
System.out.print("Please re-enter your age: ");
console.next();
}
age = console.nextInt();
System.out.println("Your age is "+age);
}
}
1/ When !console.hasNextInt() is executed for the first time, why does it ask for an input?
I thought at first the console is empty, so !console.hasNextInt() is True (empty is not an int), then it should go directly from "Please enter your age: " to "Please re-enter your age: " but my thought seems to be wrong.
Here, the user needs to enter something before "Please re-enter your age: " is printed.
2/ The data type of console.next() is always a String (I tried making int s = console.next(); and it gave an error), then why isn't this a infinite loop?
3/ For an instance, when it comes to console.next();, I input 21. Why does age have the value of 21? I thought because of console.hasNextInt(), I need to enter another number, and that new number will be the value of age.
The java.util.Scanner.hasNextInt() method 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.
When you start with a non integer input, hasNextInt() will be false and you will enter while loop. Then it will prompt you to re-enter your age. But if you start with integer, you won't enter the loop. Your age will be printed.
console.next() means it takes next input token and returns String. If you write down your code as:
String s = null;
while (!console.hasNextInt()) {
s = console.next();
System.out.println("You entered an invalid input: " + s);
System.out.print("Please re-enter your age: ");
}
console.next() is being used for handling the non-integer inputs. Now, if you enter a non-integer input twenty, you'll see that console.hasNextInt() will be false and console.next() will read it.
hasNextInt() waits for an input string and then tells you if can be converted to an int. With that in mind, let's go over your questions:
When !console.hasNextInt() is executed for the first time, why does it ask for an input?
Because it blocks until there's some input from the console.
The data type of console.next() is always a String (I tried making int s = console.next(); and it gave an error), then why isn't this a infinite loop?
Because hasNextInt() returns true when the input can be converted to an int, for example "21".
For an instance, when it comes to console.next();, I input 21. Why does age have the value of 21? I thought because of console.hasNextInt(), I need to enter another number, and that new number will be the value of age.
Calling next() doesn't wait for a new input, it just swallows the input that was tested by hasNextInt() so the scanner can move on to the next one. It could have been the first statement in the loop, with the same effect.

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.

do while loop.. to proceed if correct input is given

I am trying to do a while loop but it kept failing on me.
So while date is not entered by the user, this will keep running until a valid date is entered.
isValidDate is a method to check whether the date is true or false. Date will only be true when enter dd/mm/yy.
I am trying to put a check on whether the date is correct or not. If it is correct, it will be stored into the String date and the program will continue.
If it is incorrect, the program will then prompt the user again for the correct date.
When I first enter a incorrect date, it will then prompt me for the correct date to be entered again.
At this point, when I enter a correct date, the program could not verify if it is a correct date or not anymore. Why is this happening?
Please help!
public void bookingA(){
bkList = new ArrayList<bookingInfo>();
Scanner keys = new Scanner(System.in);
String date;
System.out.println("\nEnter the date: ");
while(!isValidDate(keys.nextLine())) {
do {
System.out.println("That is not a valid date! Please enter again (dd/mm/yy): ");
date = keys.nextLine();
} while (!isValidDate(date));
System.out.print(date);
break;
}
}
you have infinite cycle there, change it to:
System.out.println("\nEnter the date (dd/mm/yy): ");
while (true) {
date = keys.nextLine();
if (isValidDate(date)) {
break;
} else {
System.out.println("That is not a valid date! Please enter again (dd/mm/yy): ");
}
}
System.out.print(date);
Every keys.nextLine takes another input. You have to read input once into a variable then refer to the variable! As it is you are reading input three times per inner loop.
EDIT based on the code you have after your edits, there is still a problem. You still have two places where you read the input - in the while loop, and in the condition. And you still have two while loops, when you need only one. I have deleted a few lines, and added an initial assignment to date before the loop begins. That gets you this:
public void bookingA(){
bkList = new ArrayList<bookingInfo>();
Scanner keys = new Scanner(System.in);
String date;
System.out.println("\nEnter the date: ");
date = keys.nextLine();
while(!isValidDate(date)) {
System.out.println("That is not a valid date! Please enter again (dd/mm/yy): ");
date = keys.nextLine();
}
System.out.print(date);
Note - I tried to maintain some of the struture of your code (namely, a while that actually tests a changing condition) but the answer given by Milos (which has an infinite loop that you break out of when the date is valid) is probably a better solution in general since you don't need to set the date to something before entering it. On the other hand you could say that in the code I suggested your assumption is that the user has entered a valid date, and the while loop is there to confirm it, and give another chance if the date proves not to be valid.
As you can see, both solutions work, but the philosophy of one is "I am putting you in this loop and I'm not letting you out until you enter a valid date", while the other is "I will keep checking that the date you entered is valid, until it is".
But both are a lot more readable than your original code. A simple task needs a simple code structure. Worth striving for.

Categories

Resources