I've always seen a lot of confusion between (usually) beginners on the Java language about the use of Scanner. As someone who got a bit of knowledge on Java SE, I think I'm able to write a bit of content to help those who are struggling on that problem, just as I did in prior contacts.
As a use case, let's use the following code fragment:
Scanner s = new Scanner(System.in);
System.out.println("Enter your name:");
String name = s.next();
System.out.println("Enter your age:");
int age = s.nextInt();
System.out.println("Enter your height:");
double height = s.nextDouble();
System.out.printf("Hello %s, you are %n years old and %f meters tall!\n");
By reading the code, it's assumable that the program will ask a user for it's name, age and height, then print it. But when you run that code fragment and write nathan[enter]21[enter]1.68[enter]
in the terminal, things behave quite differently.
Suddenly, your dead simple program is stuck in a different situation. The values seems to just not go into the correct place, Scanner seems to read the values on a wrong order, and you don't know what to do. Depending on the string you typed you may get even stranger results.
In order to understand that situation we first need to know how Scanner works. And to explain that I will bring a bit of the official Javadocs about the Scanner class
A simple text scanner which can parse primitive types and strings using regular expressions.
A Scanner breaks its input into tokens using a delimiter pattern, which by default matches whitespace.
What we need to understand there is that scanner, by default, reads the string you put in the terminal in fragments (which are divided by whitespaces), those fragments are called “tokens”. When we execute s.next()
what the scanner do is to grab the next token in the input stream and returning it. So does nextInt()
and nextDouble()
.
public String next()
Finds and returns the next complete token from this scanner.
You may have already spot the problem there. Note that according to the documentation, scanner splits the tokens by whitespaces, and in our input (nathan[enter]21[enter]1.68[enter]
) we have NEVER wrote a single whitespace!
In that way, the scanner thinks that this entire input is just one token. And remember, the [enter] (line break) is also a character (EOL, represented by \n
).
In order to solve this problem, we have two possible alternatives:
First, the method Scanner#readLine()
can be used instead of next
, nextInt
or nextWhatever
. This method returns the input until the next line break character, instead of the next token (until whitespace). But note, this returns just string, you will have to parse that string into an int or double if you need that.
public String nextLine()
Advances this scanner past the current line and returns the input that was skipped. This method returns the rest of the current line, excluding any line separator at the end. The position is set to the beginning of the next line.
The second way is by noting that scanner uses the whitespace as delimiter by default, and that it doesn't mean that the whitespace is the only thing that can be used. To change that, we use the Scanner#useDelimiter
method.
Scanner s = new Scanner(System.in).useDelimiter("\\n")
And by doing that tweak, the program will be working as expected!