Why does the Scanner return error on something that is within expected?

Note that I typed, a number, a text and a number, as you ask there.

import java.util.Scanner;

class Ideone {
    public static void main (String[] args) {
        Scanner entrada = new Scanner(System.in);
        int valor1 = entrada.nextInt();
        String texto = entrada.nextLine();
        int valor2 = entrada.nextInt();
    }
}

See the error in ideone.

Exception in thread "main" java.util.NoSuchElementException
  at java.util.Scanner.throwFor(Scanner.java:862)
  at java.util.Scanner.next(Scanner.java:1485)
  at java.util.Scanner.nextInt(Scanner.java:2117)
  at java.util.Scanner.nextInt(Scanner.java:2076)
  at Ideone.main(Main.java:6)

Did not like this solution . I wanted to know why it gives error, what to do to solve, and if you do not have the way if Victor Stafusa's solution is the best available.

Author: Maniero, 2017-12-14

3 answers

Let's assume that your input is as follows:

5
Maniero
12

Note that nextInt() will consume 5 and return, without consuming the following line break. When nextLine() is executed, it will see the line break, consume it, and give you a blank string.

The problem is that nextInt() does not consume the following line break. To make it consume, the solution is to read the entire line (nextLine()) and use the Integer.parseInt(String) to take the number out of there.

There is an important detail in interpretation of this: what you actually want is a line containing a number, a text and another line containing a number. And if you are reading lines, use nextLine().

In addition to of my answer that you linked , I have also addressed this problem in this other answer of mine, in this other also and also in this here .

And why doesn't he consume the line breaker? Because you may want to do this, that will function:

Entry:

1 2 3

Program:

import java.util.Scanner;

class Ideone {
    public static void main (String[] args) {
        Scanner entrada = new Scanner(System.in);
        int valor1 = entrada.nextInt();
        int valor2 = entrada.nextInt();
        int valor3 = entrada.nextInt();
    }
}

The idea is that Scanner should not consume more input than is necessary to do what was asked of it. This means that nextInt() will not consume the line break that follows (or anything else that follows) because that would consume more than necessary in the input.

 16
Author: Victor Stafusa, 2017-12-14 15:21:30

The Class Scanner has several methods with the term next*, such as:

  • next() searches and returns the next full TOKEN (returns: String)

  • nextBigDecimal() scans the next TOKEN of an input and returns as BigDecimal

  • nextBigInteger() scans the next TOKEN of an input and returns as BigInteger

  • nextBoolean() parses the next TOKEN of an input into returns on a value boolean

  • nextByte() scan the next TOKEN of an input and returns as byte

  • nextDouble() scans the next TOKEN of an input and returns as double

  • nextFloat() scans the next TOKEN of an input and returns as float

  • nextInt() scans the next TOKEN of an input and returns as int

  • nextLong() scans the next TOKEN of an input and returns as long

  • nextShort() scans the next TOKEN of an input and returns as short

  • nextLine() advances this scanner beyond the current line and returns the input that was "skipped" (returns: String)

Forgetting the nextLine(), note that they all talk about such a TOKEN, " but what is the TOKEN?", Token ali refers to something that is expected, in the example nextInt expects something that has been typed that "cases" with the int, so if you do this:

Scanner entrada = new Scanner(System.in);
int valor1 = entrada.nextInt();
int valor2 = entrada.nextInt();
int valor3 = entrada.nextInt();

System.out.println("Retornou:" + valor1);
System.out.println("Retornou:" + valor2);
System.out.println("Retornou:" + valor3);

But instead of pressing Enter you Type:

1    2       3

And then only after pressing the Enter, note that it will already display the 3 System.out.println, this because both space and line break will be considered to separate the values, and these values between the separations are the TOKENS, whatever the value.

Now change to this:

Scanner entrada = new Scanner(System.in);
String valor1 = entrada.next();
String valor2 = entrada.next();
String valor3 = entrada.next();

System.out.println("Retornou:" + valor1);
System.out.println("Retornou:" + valor2);
System.out.println("Retornou:" + valor3);

And enter this all before pressing Enter :

1    2      ab

After pressing Enter, it will be displayed again all 3 System.out.println, so token in all work for spaces as much as breaking lines.

Now the nextLine

My translation got a little bad, the original text of the documentation is this:

Advances this scanner past the current line and returns the input that was skipped.

I think this text is what confuses a lot of people, when translating skipped the first time I myself was left without understanding the ignored, it's not that the line was "ignored", the meaning of skipped ali is that it passed to the next line when it executed the method, i.e. it would be more for something like "returns the input that was from the line that was skipped "(I do not know if skipped sounds good, maybe I review the text).

So actually the only one that works with the entire line instead of the TOKENs is nextLine, i.e. it is as if the entire line was a token, to explain better I will use your own code (read the comments).

Searches for the TOKEN (no need to marry int, it can be anything other than a space), but it stays on the "Line 1":

int valor1 = entrada.nextInt();

We are still in "Line 1" , but every function next* will always work after the last TOKEN, so the 1 you ignore, only the \n or \r\n of the input typed, then in the case will return an empty String, since line breaks are not values for the TOKENs and will pass to the "Line 2":

String texto = entrada.nextLine();

Now we are in "Line 2" and not in "Line 3" , but in your "Line 2" you had typed abc, which did not "house" with the next command:

int valor2 = entrada.nextInt();

Then the exception InputMismatchException occurs, following the full input

123
abc
456

 4
Author: Guilherme Nascimento, 2017-12-15 02:12:05

Wanted to know why gives error

The program gives error because the first call to entrada.nextInt() does not change lines. so when you call entrada.nextLine() the scanner will change lines and return an empty string. That is, up to this point you have to:

valor1 = 123 
texto = ""

So that when you make the second call to entrada.nextInt(), since there is no other integer in the line, an exception occurs.

What to do to solve

The resolution depends on the operation of your program, as well as, strictly establish how the user should enter the data in your application. In this case I will assume that you want to keep your input as it is.

Handle user input

In general, you should make your code so that if the user enters the data in an unexpected format, he can have information about the problem and can rectify it..

Please note that in your code there may be several other problems related to this reason. Immediately on the first call to entrada.nextInt() the program may give an error if the user enters characters other than digits.

Treat the problem immediately

If you are looking for the immediate solution, whenever you make a call to nextXXX other than nextLine() you should also call nextLine() accordingly. That is, reading a number should be done as follows way.

 int valor1 = entrada.nextInt();  
 entrada.nextLine();

Treat the problem with due care

Eventually it may be interesting to make a method with this code that eventually also treats the user's input. Something like this

private static Scanner entrada = new Scanner(System.in);
public static int readInt(String name){
    System.out.println("Introduza " + name);
    while(true){
        try{
            int valor = entrada.nextInt();
            return valor;
        }catch(Exception e){
             System.out.print(name + " deve ser um inteiro. Tente novamente.");
        }finally{
            entrada.nextLine();
        }
    }    
}   

This code may have a lot of defects, but it certainly at least demonstrates a greater tolerance to invalid input on the part of the user.

 1
Author: Bruno Costa, 2018-06-30 23:57:53