Scanner gives error if inverting the order in which data is read

Asked

Viewed 159 times

2

When I put to read first the int and then the chain of string, the system works smoothly, but when I invert it gives an error and does not finish.

import java.util.Scanner;
class main {
    public static void main(String[] args) {

        int idade, cont, maiorIdade;
        String nome, maisVelho;
        idade = 0;
        cont = 0;
        maiorIdade = 0;
        maisVelho = "a";

        Scanner ler = new Scanner(System.in);

        while (cont < 5){

            nome = ler.nextLine();
            idade = ler.nextInt();

            if ((nome != "a")&&(idade > maiorIdade)){
                maisVelho = nome;
                maiorIdade = idade;
            }
            cont++;
        }
        System.out.println(maisVelho);
    }
}

Descriptive image:

Quando eu coloco para ler primeiro a idade e depois a string, o programa funciona, mas quando eu troco as posições de leitura aparece esse erro ai

  • 2

    Just to warn you, comparing strings to operator is not a good one, I recommend using the string class equals method.

  • Probably the error is at the time of reading the age, because it is expected number and was typed text. The order in which Voce enters the data must be the same as the order in which the variables are completed.

3 answers

2

Let’s run some tests to see what happens to each reading done by Scanner, and so understand why "works" in some cases and in others not.


First let’s see what happens when you read the number before the name. To better understand, I put a System.out.println to see the value of each variable right after it is read by Scanner:

idade = ler.nextInt();
System.out.println("idade=" + idade);
nome = ler.nextLine();
System.out.println("nome=" + nome);

If you type 10 Fulano, what happens is this:

  • nextInt read the number 10
  • nextLine reads from the position in which it stopped earlier (i.e., right after the 10) until the end of the line

So the exit is:

idade=10
nome= Fulano

Note that the name has a space at the beginning (before the F), since the nextLine() begins reading the position in which it stopped (which is just after the 10), and ends at the end of the line.


Now, if you reverse the order in which the data is read:

nome = ler.nextLine();
System.out.println("nome=" + nome);
idade = ler.nextInt();
System.out.println("idade=" + idade);

If you type Fulano 10, the call of nextLine() will get whole line (the variable nome will have all the string Fulano 10) and the code will print:

nome=Fulano 10

When you type the next line (for example, Ciclano 20), the code will try to interpret it with nextInt(), but how this line starts with Ciclano and this is not a number, will occur the InputMismatchException.

You can even change the call from nextLine() for next(), that instead of picking up the whole line, it only picks up to space (which is the delimiter default, according to the documentation):

nome = ler.next();
System.out.println("nome=" + nome);
idade = ler.nextInt();
System.out.println("idade=" + idade);

Now if you type Fulano 10, the name will be "John Doe" and the age will be 10.


But the above solution assumes that the name has no spaces. If you type Fulano de Tal 10, for example, the next() will just take Fulano, and the nextInt() will try to read the excerpt de as a number, and will give error.

In that case, it might be easier to read the whole line with nextLine(), make a split and see if the number is at the beginning or the end (and then consider that the name is what’s left).

Another detail is that you do not need to compare the name of the elder within the loop. If you want to know the age of the oldest, just compare the ages and then you keep the respective name. In the end it looks like this:

int idade = 0;
int cont = 0;
int maiorIdade = -1;
String nome, maisVelho = "";

Scanner ler = new Scanner(System.in);
while (cont < 5) {
    // divide a linha em partes, separadas por espaço
    String[] partes = ler.nextLine().split(" ");
    try {
        // ver se o número está no começo
        idade = Integer.parseInt(partes[0]);
        // juntar as partes restantes para compor o nome (do segundo ao último elemento)
        nome = String.join(" ", Arrays.copyOfRange(partes, 1, partes.length));
    } catch (NumberFormatException e) {
        // número está no final
        idade = Integer.parseInt(partes[partes.length - 1]);
        // pegar do primeiro ao penúltimo elemento (juntar para obter o nome)
        nome = String.join(" ", Arrays.copyOfRange(partes, 0, partes.length - 1));
    }
    System.out.println("nome=" + nome);
    System.out.println("idade=" + idade);

    if (idade > maiorIdade) {
        maisVelho = nome;
        maiorIdade = idade;
    }
    cont++;
}
System.out.println("mais velho=" + maisVelho + ", idade=" + maiorIdade);

Now it doesn’t matter if the line is typed as 10 Fulano de Tal or Ciclano da Silva 30, because the code tries to verify the number at the beginning, and if it does not give, checks at the end (and if it is not at the end, will give error, which you can choose to capture by placing another try/catch, if you want).

Then the method of join (available from Java 8) joins the other parts to form the name.

And the if who compares the ages do not need to compare the name, because you only need to check if in the new data entered the age is greater (and then you arrow the respective name).


Another alternative, in case the name comes first and can have spaces, is to change the tab default that the Scanner uses:

Scanner ler = new Scanner(System.in).useDelimiter(" (?=\\d)|\\n");
while (cont < 5) {
    nome = ler.next();
    idade = ler.nextInt();
... etc

Thus, the method next uses the specified regular expression as a separator. In this case, it has a space (note that there is a space after the opening of the quotation marks) and then we have a Lookahead (the stretch (?=\\d)), that serves to verify if something exists after the current position. This indicates that I use as separator a space, as long as it has a digit (\\d) soon after.

But regex also uses alternation (the character |), indicating that you can also use the \n (line break) as separator.

That is, the method next() reads everything until you find a space (as long as it’s followed by number) or a line break. With this, lines like Fulano de Tal 10 are read correctly: first the next() capture the string Fulano de Tal and then nextInt() capture the 10.

But if you want a more generic solution, in which it does not matter if the number comes at the beginning or at the end, better use the previous solution, with split.

0

I listed here some changes I had to make to your code.

  1. Class name must start with uppercase letter.

  2. You can declare variables and start them on the same line.

  3. I added some logs to indicate what the code is asking for.

  4. For reading strings instead of using nextLine() can only use the next().

  5. Comparison of strings in Java has to be done through the method equals.

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        int idade = 0, cont = 0, maiorIdade = 0;
        String nome = "", maisVelho = "";

        Scanner ler = new Scanner(System.in);

        while (cont < 5){
            System.out.print("Digite seu nome: ");
            nome = ler.next();

            System.out.print("Digite sua idade: ");
            idade = ler.nextInt();

            if (!nome.equals(maisVelho) && idade > maiorIdade) {
                maisVelho = nome;
                maiorIdade = idade;
            }

            cont++;
        }

        System.out.printf("O mais velho é %s com a idade de: %d\n", maisVelho, maiorIdade);
    }
}

0

As quoted in the other answers, there are a few points that can be improved in your code, but to be direct in your problem:

You cannot simply change the order of the parameters once you specify the order of the readings (nextLine and nextInt).
Thus, the parameters should be passed in that order: String (to the nextLine) and int (to the nextInt).

In addition, the use of nextLine implies taking the reading cursor to the next line. As if you were passing the name and age in different lines.
From what I understand, that’s not what you want, right?
That way, use the next() instead of nextLine().


If you still want to pass the parameters in random orders, you should treat the receipt of these parameters.
To do this, you can receive both through the method next() and none by the method nextInt() just by not knowing if the parameter being received is an integer value or not.
From there, you need to try to identify and convert this data (but there is another vast subject matter)


Some references:

Browser other questions tagged

You are not signed in. Login or sign up in order to post.