Java:使用 Scanner in.hasNextInt() 的无限循环

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1794281/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 22:43:12  来源:igfitidea点击:

Java: Infinite loop using Scanner in.hasNextInt()

javaloopsjava.util.scannerinfinite

提问by Tomek

I am using the following code:

我正在使用以下代码:

while (invalidInput)
{
    // ask the user to specify a number to update the times by
    System.out.print("Specify an integer between 0 and 5: ");

    if (in.hasNextInt())
    {
        // get the update value
        updateValue = in.nextInt();

        // check to see if it was within range
        if (updateValue >= 0 && updateValue <= 5) 
        { 
            invalidInput = false; 
        } 
        else 
        {
            System.out.println("You have not entered a number between 0 and 5. Try again.");
        }
    } else
    {
        System.out.println("You have entered an invalid input. Try again.");
    }
}

However, if I enter a 'w' it will tell me "You have entered invalid input. Try Again." and then it will go into an infinite loop showing the text "Specify an integer between 0 and 5: You have entered an invalid input. Try again."

但是,如果我输入'w',它会告诉我“您输入了无效的输入。再试一次。” 然后它将进入无限循环,显示文本“指定 0 到 5 之间的整数:您输入了无效输入。再试一次。”

Why is this happening? Isn't the program supposed to wait for the user to input and press enter each time it reaches the statement:

为什么会这样?程序是不是应该等待用户输入并在每次到达语句时按 Enter 键:

if (in.hasNextInt())

采纳答案by Kaleb Brasee

In your last elseblock, you need to clear the 'w' or other invalid input from the Scanner. You can do this by calling next()on the Scanner and ignoring its return value to throw away that invalid input, as follows:

在您的最后一个else块中,您需要从扫描仪中清除 'w' 或其他无效输入。您可以通过调用next()Scanner 并忽略其返回值以丢弃该无效输入来执行此操作,如下所示:

else
{
      System.out.println("You have entered an invalid input. Try again.");
      in.next();
}

回答by Ben S

Flag variables are too error prone to use. Use explicit loop control with comments instead. Also, hasNextInt()does not block. It's the non-blocking check to see if a future nextcall could get input without blocking. If you want to block, use the nextInt()method.

标志变量使用起来太容易出错。使用带有注释的显式循环控制代替。此外,hasNextInt()不阻塞。这是非阻塞检查,以查看未来的next调用是否可以在不阻塞的情况下获取输入。如果要阻止,请使用该nextInt()方法。

// Scanner that will read the integer
final Scanner in = new Scanner(System.in);
int inputInt;
do {  // Loop until we have correct input
    System.out.print("Specify an integer between 0 and 5: ");
    try {
        inputInt = in.nextInt(); // Blocks for user input
        if (inputInt >= 0 && inputInt <= 5)  { 
            break;    // Got valid input, stop looping
        } else {
            System.out.println("You have not entered a number between 0 and 5. Try again.");
            continue; // restart loop, wrong number
         }
    } catch (final InputMismatchException e) {
        System.out.println("You have entered an invalid input. Try again.");
        in.next();    // discard non-int input
        continue;     // restart loop, didn't get an integer input
    }
} while (true);

回答by polygenelubricants

The problem was that you did not advance the Scannerpast the problematic input. From hasNextInt()documentation:

问题是你没有Scanner把有问题的输入提前过去。从hasNextInt()文档:

Returns trueif the next token in this scanner's input can be interpreted as an intvalue in the default radix using the nextInt()method. The scanner does not advance past any input.

返回true此扫描器输入中的下一个标记是否可以int使用该nextInt()方法解释为默认基数中的值。扫描仪不会通过任何输入。

This is true of all hasNextXXX()methods: they return trueor false, without advancing the Scanner.

所有hasNextXXX()方法都是如此:它们返回trueor false,而不推进Scanner.

Here's a snippet to illustrate the problem:

这里有一个片段来说明这个问题:

    String input = "1 2 3 oops 4 5 6";
    Scanner sc = new Scanner(input);
    while (sc.hasNext()) {
        if (sc.hasNextInt()) {
            int num = sc.nextInt();
            System.out.println("Got " + num);
        } else {
            System.out.println("int, please!");
            //sc.next(); // uncomment to fix!
        }
    }

You will find that this program will go into an infinite loop, asking int, please!repeatedly.

你会发现这个程序会进入无限循环,int, please!反复询问。

If you uncomment the sc.next()statement, then it will make the Scannergo past the token that fails hasNextInt(). The program would then print:

如果您取消注释该sc.next()语句,那么它将使Scanner失败的令牌过去hasNextInt()。然后程序会打印:

Got 1
Got 2
Got 3
int, please!
Got 4
Got 5
Got 6

The fact that a failed hasNextXXX()check doesn't skip the input is intentional: it allows you to perform additional checks on that token if necessary. Here's an example to illustrate:

失败的hasNextXXX()检查不会跳过输入的事实是有意的:它允许您在必要时对该令牌执行额外的检查。这里有一个例子来说明:

    String input = " 1 true foo 2 false bar 3 ";
    Scanner sc = new Scanner(input);
    while (sc.hasNext()) {
        if (sc.hasNextInt()) {
            System.out.println("(int) " + sc.nextInt());
        } else if (sc.hasNextBoolean()) {
            System.out.println("(boolean) " + sc.nextBoolean());
        } else {
            System.out.println(sc.next());
        }
    }

If you run this program, it will output the following:

如果你运行这个程序,它会输出以下内容:

(int) 1
(boolean) true
foo
(int) 2
(boolean) false
bar
(int) 3

回答by Rich Fletcher

This statement by Ben S. about the non-blocking call is false:

Ben S. 关于非阻塞调用的声明是错误的:

Also, hasNextInt() does not block. It's the non-blocking check to see if a future next call could get input without blocking.

此外, hasNextInt() 不会阻塞。这是非阻塞检查,以查看未来的下一个调用是否可以在不阻塞的情况下获取输入。

...although I do recognize that the documentationcan easily be misread to give this opinion, and the name itself implies it is to be used for this purpose. The relevant quote, with emphasis added:

...虽然我确实认识到文档很容易被误读以给出这种意见,并且名称本身暗示它用于此目的。相关引述,重点补充:

The next() and hasNext() methods and their primitive-type companion methods (such as nextInt() and hasNextInt())first skip any input that matches the delimiter pattern, and then attempt to return the next token. Both hasNext and next methods may block waiting for further input.Whether a hasNext method blocks has no connection to whether or not its associated next method will block.

next() 和 hasNext() 方法及其原始类型的伴随方法(例如 nextInt() 和 hasNextInt())首先跳过与分隔符模式匹配的任何输入,然后尝试返回下一个标记。hasNext 和 next 方法都可能阻塞等待进一步的输入。hasNext 方法是否阻塞与其关联的 next 方法是否会阻塞无关。

It is a subtle point, to be sure. Either saying "Both thehasNext and next methods", or "Both hasnext() and next()" would have implied that the companion methods would act differently. But seeing as they conform to the same naming convention (and the documentation, of course), it's reasonable to expect they act the same, and hasNext()clearly says that it can block.

这是一个微妙的点,可以肯定。无论是说“无论是hasNext和next方法”或“两者hasnext()和next()”会暗示伴侣的方法将采取不同。但是看到它们符合相同的命名约定(当然还有文档),期望它们的行为相同是合理的,而且hasNext()明确表示它可以阻塞。

Meta note: this should probably be a comment to the incorrect post, but it seems that as a new user I can only post this answer (or edit the wiki which seems to be preferred for sytlistic changes, not those of substance).

元注释:这可能是对错误帖子的评论,但作为新用户,我似乎只能发布此答案(或编辑似乎更适合进行语法更改的维基,而不是实质更改)。