Infinite loop when reading integers with Scanner

Asked

Viewed 280 times

0

I’m trying to treat an exception within a repeating structure for, but I am having difficulties, because the catch is not returning to the for, and by typing an invalid value, it is in an infinite loop showing the error message.

Follow the code below:

public class Courses {

    private static Set<Integer> listOfStudents = new HashSet<>();

    public static void calculate(Integer students, Scanner sc) {
        for (int c = 0; c < students; c++) {
            try {
                listOfStudents.add(sc.nextInt());
            } catch (InputMismatchException e) {
                System.out.println("Invalid code! Try again");
                c--;
            }
        }
    }

    public static int getNumberOfStudents() {
        return listOfStudents.size();
    }

}

'c--' serves to not lose the amount of student code to be typed, for example:

  • If I type 3 students, and pass an invalid value, it will decrease 1 in for and ask to enter a valid value.
  • *it is in an infinite loop *, yes it will be, look at your code, if it is wrong you decrement the variable "c", and what it does in for? increments again. To handle this, the invalid value, it would be better to place this block inside a while, as a condition of running until the value is valid

  • Yes, I understood, but this is exactly what I want to do, let’s say that the 'c' is worth 0, when a mistake is made, it will go to -1, and will be increased to 0 again (so I don’t miss the "time" to register a student), so I should enter the 'Try' block novamento, but he gets in a loop on the catch. I’m sorry if I misunderstood you, but could you show me an example if that’s the case?

  • only that loops are not made to change the value of the variable in this way, it is a continuous counter and should not move it, for what you want to do there are other means, as for example what I said, use a loop while which will only come out if the value is valid, without changing the value of the for, try to do it that way

  • I did it with 'while' and it keeps giving the same problem. Leave the 'while' inside the 'for', and inside there is the 'Try/catch' block, and still keep giving that loop. Is there something specific about catch that I don’t know? I’m kind of new to this area yet

  • @Ricardopunctual to variable int c is a simple variable like any other, only visible in Scope of for, can be used like other variables. This way, you should not receive a "special treatment".

  • 1

    I’m just saying that the purpose of for is to be a linear counter, going from 1 to 10, etc... if you need to keep changing a counter, the for is not the best option, but changing the value of the variable can, but is not the best way to control the loop :)

  • Aaaah understood, so I can change the value of the variable of the 'for', but it is not advisable, it is neh?

Show 2 more comments

2 answers

0


The "infinite loop" happens once the method Scanner::nextInt does not consume the input which generated the error, hence the buffer shall remain with what is written until it is consumed. Thus, within the catch the input wrong through the call to method Scanner::nextLine.

public static void calculate(int students, Scanner sc) {
    for (int c = 0; c < students; ) {
        try {
            listOfStudents.add(sc.nextInt());
            ++c;
        } catch (InputMismatchException e) {
            sc.nextLine();
            System.out.println("Invalid code! Try again");
        }
    }
}

Note: For simplification you can put the c++ only after the reading has been successfully done, thus removing the c--.

  • I understood, but I didn’t know that this kind of implementation could be done in for, that is, putting c++ after a certain condition and so on, there is some specific name for this trick?

0

First let’s understand this example code:

Scanner sc = new Scanner(System.in);
try {
    System.out.println("digite um número: ");
    System.out.println(sc.nextInt());
} catch (InputMismatchException e) {
    System.out.println("deu erro, tente novamente: ");
    System.out.println(sc.nextInt());
}

Suppose in the first nextInt() i do not enter a number. The output will be:

digite um número: 
abc
deu erro, tente novamente: Exception in thread "main" 
java.util.InputMismatchException
    at java.util.Scanner.throwFor(Scanner.java:864)
    at java.util.Scanner.next(Scanner.java:1485)
    at java.util.Scanner.nextInt(Scanner.java:2117)
    at java.util.Scanner.nextInt(Scanner.java:2076)
    ...

What happens is, since I didn’t type a number, the first nextInt() makes the exception and the scanner does not advance in the string (it remains stopped before the abc). Then when I call nextInt() again, he tries to read again the abc, and gives error again (InputMismatchException).

That’s what’s happening in your loop. When the nextInt() receives an invalid value, falls within the catch (and the scanner does not advance the string). Then it goes to the next iteration of for and the nextInt() tries to read the invalid value previously typed (and falls inside the catch again, and back to the nextInt, that falls in the catch...). And as inside the catch you decrement the control variable of the loop, he never leaves the for.

What you can do is call for nextLine() while the entered value is not a number (what can be verified with hasNextInt()), thus the scanner skip the invalid section:

for (int c = 0; c < students; c++) {
    try {
        while(! sc.hasNextInt()) {
            // consome a entrada até que seja encontrado um número
            sc.nextLine();
        }
        listOfStudents.add(sc.nextInt());
    } catch (InputMismatchException e) {
        System.out.println("Invalid code! Try again");
    }
}

So I guarantee that the nextInt() will only be called when the next value to be read is a number. Note also that now you do not need to be decreasing the c, because it only goes to the next iteration of for when you can read a number.

  • That’s right, it worked out here, nice explanation :)

Browser other questions tagged

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