Java:Loop infinito usando Scanner in.hasNextInt()
-
22-09-2019 - |
Pergunta
Estou usando o seguinte código:
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.");
}
}
No entanto, se eu inserir um 'w', ele me dirá "Você digitou uma entrada inválida.Tente novamente." e então entrará em um loop infinito mostrando o texto "Especifique um número inteiro entre 0 e 5:Você inseriu uma entrada inválida.Tente novamente."
Por que isso está acontecendo?O programa não deveria esperar que o usuário insira e pressione Enter cada vez que chegar à instrução:
if (in.hasNextInt())
Solução
Em seu último else
Bloco, você precisa limpar o 'W' ou outra entrada inválida do scanner. Você pode fazer isso ligando next()
no scanner e ignorando seu valor de retorno para jogar fora essa entrada inválida, como segue:
else
{
System.out.println("You have entered an invalid input. Try again.");
in.next();
}
Outras dicas
O problema era que você não avançou no Scanner
após a entrada problemática. A partir de hasNextInt()
documentação:
Retorna
true
Se o próximo token na entrada deste scanner puder ser interpretado como umint
valor no Radix padrão usando onextInt()
método. O scanner não avança em nenhuma entrada.
Isso é verdade para todos hasNextXXX()
Métodos: eles retornam true
ou false
, sem avançar o Scanner
.
Aqui está um trecho para ilustrar o problema:
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!
}
}
Você descobrirá que este programa entrará em um loop infinito, perguntando int, please!
repetidamente.
Se você descomentar o sc.next()
declaração, então fará o Scanner
Passe o token que falha hasNextInt()
. O programa imprimiria:
Got 1
Got 2
Got 3
int, please!
Got 4
Got 5
Got 6
O fato de que um falhou hasNextXXX()
A verificação não ignora a entrada é intencional: permite que você realize verificações adicionais nesse token, se necessário. Aqui está um exemplo para ilustrar:
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());
}
}
Se você executar este programa, ele produzirá o seguinte:
(int) 1
(boolean) true
foo
(int) 2
(boolean) false
bar
(int) 3
Esta afirmação de Ben S. sobre a chamada não bloqueadora é falsa:
Além disso, o hasNextInt () não bloqueia. É a verificação que não é bloqueadora para ver se uma próxima chamada futura pode obter entrada sem bloquear.
... embora eu reconheça que o documentação pode ser facilmente interpretado para dar essa opinião, e o próprio nome implica que deve ser usado para esse fim. A citação relevante, com ênfase adicionada:
Os próximos métodos () e hasnext () e seus métodos complementares do tipo primitivo (como NextInt () e HasNextInt ()) Primeiro pule qualquer entrada que corresponda ao padrão delimitador e depois tente retornar o próximo token. Tanto o HasNext quanto os próximos métodos podem bloquear a espera por mais informações. Se um método HasNext blocos não tem conexão se o próximo método associado será bloqueado ou não.
É um ponto sutil, com certeza. Ou dizendo "ambos a Hasnext e os próximos métodos ", ou" HasNext () e Next () "teriam implícito que os métodos complementares agiriam de maneira diferente. Mas, visto que estão em conformidade com a mesma convenção de nomenclatura (e a documentação, é claro), é razoável Espere que eles agem da mesma forma, e hasNext ()diz claramente que pode bloquear.
META NOTA: Isso provavelmente deve ser um comentário para a postagem incorreta, mas parece que, como um novo usuário, só posso postar essa resposta (ou editar o wiki que parece ser preferido para alterações sistematizadas, não as de substância).
Variáveis de sinalização são muito propensas a erros para serem usadas.Use controle de loop explícito com comentários.Também, hasNextInt()
não bloqueia.É a verificação sem bloqueio para ver se um futuro next
a chamada pode obter entrada sem bloqueio.Se você quiser bloquear, use o nextInt()
método.
// 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);