Validando input usando java.util.Scanner

Eu estou tomando input do usuário do System.in usando um java.util.Scanner . Eu preciso validar a input para coisas como:

  • Deve ser um número não negativo
  • Deve ser uma letra alfabética
  • … etc

Qual é a melhor forma de fazer isso?

Visão geral dos methods Scanner.hasNextXXX

java.util.Scanner tem muitos methods hasNextXXX que podem ser usados ​​para validar input. Aqui está uma breve visão geral de todos eles:

  • hasNext() – tem algum token?
  • hasNextLine() – tem outra linha de input?
  • Para primitivos Java
    • hasNextInt() – possui um token que pode ser analisado em um int ?
    • Também estão disponíveis hasNextDouble() , hasNextFloat() , hasNextByte() , hasNextShort() , hasNextLong() e hasNextBoolean()
    • Como bônus, também tem hasNextBigInteger() e hasNextBigDecimal()
    • Os tipos integrais também possuem sobrecargas para especificar radix (por exemplo, hexadecimal)
  • Baseado em expressões regulares
    • hasNext(String pattern)
    • hasNext(Pattern pattern) é a sobrecarga do Pattern.compile

Scanner é capaz de mais, habilitado pelo fato de ser baseado em regex. Uma característica importante é o useDelimiter(String pattern) , que permite definir qual padrão separa seus tokens. Também existem e skip methods que ignoram os delimitadores.

A discussão a seguir manterá a regex o mais simples possível, de modo que o foco permaneça no Scanner .


Exemplo 1: Validando Ints Positivos

Aqui está um exemplo simples de usar hasNextInt() para validar int positivo da input.

 Scanner sc = new Scanner(System.in); int number; do { System.out.println("Please enter a positive number!"); while (!sc.hasNextInt()) { System.out.println("That's not a number!"); sc.next(); // this is important! } number = sc.nextInt(); } while (number <= 0); System.out.println("Thank you! Got " + number); 

Aqui está um exemplo de session:

Por favor, insira um número positivo!
cinco
Isso não é um número!
-3
Por favor, insira um número positivo!
5
Obrigado! Tenho 5

Observe o quanto é mais fácil usar o Scanner.hasNextInt() em comparação com a combinação mais detalhada try/catch Integer.parseInt / NumberFormatException . Por contrato, um Scanner garante que, se ele tiver hasNextInt() , nextInt() fornecerá pacificamente essa int e não lançará nenhuma NumberFormatException / InputMismatchException / NoSuchElementException .

Perguntas relacionadas

  • Como usar o Scanner para aceitar apenas int válido como input
  • Como evito que um scanner lance exceções quando o tipo errado é typescript? (Java)

Exemplo 2: vários hasNextXXX no mesmo token

Observe que o snippet acima contém uma sc.next() para avançar o Scanner até que ele tenha hasNextInt() . É importante perceber que nenhum dos methods hasNextXXX avança o Scanner além de qualquer input! Você descobrirá que, se omitir essa linha do snippet, ela entrará em um loop infinito em uma input inválida!

Isso tem duas conseqüências:

  • Se você precisar pular a input "lixo" que falha no seu teste hasNextXXX , então você precisa avançar o Scanner uma forma ou de outra (por exemplo, next() , nextLine() , skip , etc).
  • Se um teste hasNextXXX falhar, você ainda pode testar se ele talvez tenha hasNextYYY !

Aqui está um exemplo de execução de vários testes hasNextXXX .

 Scanner sc = new Scanner(System.in); while (!sc.hasNext("exit")) { System.out.println( sc.hasNextInt() ? "(int) " + sc.nextInt() : sc.hasNextLong() ? "(long) " + sc.nextLong() : sc.hasNextDouble() ? "(double) " + sc.nextDouble() : sc.hasNextBoolean() ? "(boolean) " + sc.nextBoolean() : "(String) " + sc.next() ); } 

Aqui está um exemplo de session:

5
(int) 5
falso
(boolean) false
blá
(String) blah
1,1
(duplo) 1,1
100000000000
(longa) 100000000000
Saída

Note que a ordem dos testes é importante. Se um Scanner hasNextInt() , então também hasNextLong() , mas não é necessariamente true ao contrário. Mais frequentemente do que não você gostaria de fazer o teste mais específico antes do teste mais geral.


Exemplo 3: validando vogais

Scanner possui muitos resources avançados suportados por expressões regulares. Aqui está um exemplo de usá-lo para validar as vogais.

 Scanner sc = new Scanner(System.in); System.out.println("Please enter a vowel, lowercase!"); while (!sc.hasNext("[aeiou]")) { System.out.println("That's not a vowel!"); sc.next(); } String vowel = sc.next(); System.out.println("Thank you! Got " + vowel); 

Aqui está um exemplo de session:

Por favor insira uma vogal, em minúsculas!
5
Isso não é uma vogal!
z
Isso não é uma vogal!
e
Obrigado! Tenho e

No regex, como um literal de string Java, o padrão "[aeiou]" é o que é chamado de "class de caractere"; coincide com qualquer uma das letras a , e , i , o , u . Note que é trivial tornar o teste acima insensível a maiúsculas e minúsculas: basta fornecer esse padrão de expressão regular ao Scanner .

Links de API

  • hasNext(String pattern) - Retorna true se o próximo token corresponder ao padrão construído a partir da string especificada.
  • java.util.regex.Pattern

Perguntas relacionadas

  • Lendo um único char em Java

Referências

  • Tutoriais Java / Classes Essenciais / Expressões Regulares
  • regular-expressions.info/Character Classes

Exemplo 4: Usando dois Scanner uma só vez

Às vezes, você precisa digitalizar linha por linha, com vários tokens em uma linha. A maneira mais fácil de fazer isso é usar dois nextLine() , onde o segundo Scanner pega o nextLine() do primeiro Scanner como input. Aqui está um exemplo:

 Scanner sc = new Scanner(System.in); System.out.println("Give me a bunch of numbers in a line (or 'exit')"); while (!sc.hasNext("exit")) { Scanner lineSc = new Scanner(sc.nextLine()); int sum = 0; while (lineSc.hasNextInt()) { sum += lineSc.nextInt(); } System.out.println("Sum is " + sum); } 

Aqui está um exemplo de session:

Dá-me um monte de números em uma linha (ou 'sair')
3 4 5
Soma é 12
10 100 um milhão de dólares
A sum é 110
espere o que?
Soma é 0
Saída

Além do construtor Scanner(String) , também há o Scanner(java.io.File) entre outros.


Resumo

  • Scanner fornece um rico conjunto de resources, como methods hasNextXXX para validação.
  • O uso adequado de hasNextXXX/nextXXX em combinação significa que um Scanner NUNCA lançará um InputMismatchException / NoSuchElementException .
  • Lembre-se sempre de que o hasNextXXX não avança o Scanner após qualquer input.
  • Não tenha vergonha de criar vários Scanner se necessário. Dois Scanner simples costumam ser melhores do que um Scanner excessivamente complexo.
  • Por fim, mesmo que você não tenha planos de usar os resources avançados de regex, tenha em mente quais methods são baseados em regex e quais não são. Qualquer método de Scanner que receba um argumento de String pattern é baseado em regex.
    • Dica : uma maneira fácil de transformar qualquer String em um padrão literal é Pattern.quote .

Aqui está uma maneira minimalista de fazer isso.

 System.out.print("Please enter an integer: "); while(!scan.hasNextInt()) scan.next(); int demoInt = scan.nextInt(); 

Para verificar Strings para letras, você pode usar expressões regulares, por exemplo:

 someString.matches("[AF]"); 

Para verificar números e parar o programa, tenho uma class bem simples que você pode encontrar abaixo, onde você pode definir o intervalo de valores que você deseja. Aqui

  public int readInt(String prompt, int min, int max) { Scanner scan = new Scanner(System.in); int number = 0; //Run once and loop until the input is within the specified range. do { //Print users message. System.out.printf("\n%s > ", prompt); //Prevent string input crashing the program. while (!scan.hasNextInt()) { System.out.printf("Input doesn't match specifications. Try again."); System.out.printf("\n%s > ", prompt); scan.next(); } //Set the number. number = scan.nextInt(); //If the number is outside range print an error message. if (number < min || number > max) System.out.printf("Input doesn't match specifications. Try again."); } while (number < min || number > max); return number; } 

Uma ideia:

 try { int i = Integer.parseInt(myString); if (i < 0) { // Error, negative input } } catch (NumberFormatException e) { // Error, not a number. } 

Há também, na biblioteca commons-lang , a class CharUtils que fornece os methods isAsciiNumeric() para verificar se um caractere é um número e isAsciiAlpha() para verificar se o caractere é uma letra ...

Se você estiver analisando dados de string do console ou similar, a melhor maneira é usar expressões regulares. Leia mais sobre isso aqui: http://java.sun.com/developer/technicalArticles/releases/1.4regex/

Caso contrário, para analisar um int de uma cadeia, tente Integer.parseInt (string) . Se a string não for um número, você receberá uma exceção. Além disso, você pode executar suas verificações nesse valor para garantir que ele não seja negativo.

 String input; int number; try { number = Integer.parseInt(input); if(number > 0) { System.out.println("You positive number is " + number); } } catch (NumberFormatException ex) { System.out.println("That is not a positive number!"); } 

Para obter uma string somente de caractere, você provavelmente seria melhor fazer um loop sobre cada caractere procurando por dígitos, usando por exemplo Character.isLetter (char) .

 String input for(int i = 0; i 

Boa sorte!

o que eu tentei é que primeiro eu tomei a input de inteiro e verifiquei se é negativo ou não se o seu negativo, em seguida, novamente tomar a input

 Scanner s=new Scanner(System.in); int a=s.nextInt(); while(a<0) { System.out.println("please provide non negative integer input "); a=s.nextInt(); } System.out.println("the non negative integer input is "+a); 

Aqui, você precisa pegar a input de caractere primeiro e verificar se o usuário deu caractere ou não, senão, novamente, pegar a input de caractere

  char ch = s.findInLine(".").charAt(0); while(!Charcter.isLetter(ch)) { System.out.println("please provide a character input "); ch=s.findInLine(".").charAt(0); } System.out.println("the character input is "+ch);