web-dev-qa-db-ja.com

Java:スキャナーin.hasNextInt()を使用した無限ループ

私は次のコードを使用しています:

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.");
    }
}

ただし、「w」を入力すると、「無効な入力を入力しました。再試行してください」と表示されます。次に、「0から5までの整数を指定してください:無効な入力を入力しました。再試行してください。」というテキストを示す無限ループに入ります。

なんでこんなことが起こっているの?プログラムは、ステートメントに到達するたびにユーザーが入力してEnterキーを押すのを待つことになっているのではありません。

if (in.hasNextInt())
14
Tomek

最後のelseブロックで、スキャナーからの「w」またはその他の無効な入力をクリアする必要があります。これを行うには、次のように、スキャナーでnext()を呼び出し、その戻り値を無視して、無効な入力を破棄します。

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

問題は、問題のある入力を超えてScannerを進めなかったことです。 hasNextInt() ドキュメントから:

このスキャナーの入力の次のトークンが、nextInt()メソッドを使用して、デフォルトの基数のtrue値として解釈できる場合、intを返します。 スキャナーは入力を超えて進みません。

これはすべてのhasNextXXX()メソッドに当てはまります。trueを進めずにfalseまたはScannerを返します。

問題を説明するためのスニペットは次のとおりです。

_    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!
        }
    }
_

このプログラムは無限ループに入り、_int, please!_を繰り返し要求します。

sc.next()ステートメントのコメントを外すと、ScannerhasNextInt()に失敗したトークンを通過します。その後、プログラムは次のように出力します。

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

失敗した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());
        }
    }
_

このプログラムを実行すると、次のように出力されます。

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

ノンブロッキングコールに関するBenS。のこの声明は誤りです。

また、hasNextInt()はブロックしません。これは、将来の次の呼び出しがブロックせずに入力を取得できるかどうかを確認するための非ブロックチェックです。

...私は documentation がこの意見を与えるために簡単に誤解される可能性があることを認識していますが、名前自体はそれがこの目的のために使用されることを意味します。関連する引用、強調を追加:

Next()メソッドとhasNext()メソッドおよびそれらのプリミティブ型のコンパニオンメソッド(nextInt()やhasNextInt())は、最初に次の入力をスキップします。区切り文字パターンに一致してから、次のトークンを返そうとします。 hasNextメソッドとnextメソッドの両方が、さらなる入力の待機をブロックする場合があります。hasNextメソッドがブロックするかどうかは、関連するnextメソッドがブロックするかどうかとは関係ありません。

確かに、それは微妙な点です。 「thehasNextメソッドとnextメソッドの両方」または「hasnext()とnext()の両方」と言うと、コンパニオンメソッドの動作が異なることを意味します。 。しかし、それらが同じ命名規則(そしてもちろんドキュメント)に準拠していることを考えると、それらが同じように動作することを期待するのは合理的であり、 hasNext() はブロックできることを明確に示しています。

メタノート:これはおそらく間違った投稿へのコメントであるはずですが、新しいユーザーとして私はこの回答しか投稿できないようです(または、実質的な変更ではなく、体系的な変更に適していると思われるwikiを編集します)。

4
Rich Fletcher

フラグ変数はエラーが発生しやすく、使用できません。代わりに、コメント付きの明示的なループ制御を使用してください。また、 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);
1
Ben S