System.out
é um objeto. Este objeto sabe como escrever informação no standard output (em geral o ecrã). Quando invocamos os métodos print
e println
, invocamo-los sobre este objeto, ou seja, o System.out
é o alvo da invocação destes métodos.
Também existe um objeto chamado System.in
que permite ler valores do standard input (em geral o teclado). No entanto, este objeto não torna fácil essa leitura de valores a partir do teclado.
Vamos usar um outro objeto que nos vai facilitar a interpretação da informação obtida pelo System.in
: uma instância da classe Scanner
(do pacote java.util
) .
Este objeto vai-nos ajudar a “trazer para dentro do nosso programa” a informação que o utilizador digita no teclado.
Consulte a documentação da classe Scanner
do pacote java.util
em https://docs.oracle.com/javase/9/docs/api/java/util/Scanner.html.
Scanner
Um objeto
Scanner
é um objeto que
- sabe como ir buscar um pedaço de informação – token (sequência de carateres delimitada por um dado delimitador) – a uma dada fonte de carateres, ou canal de leitura, e
- torna essa token acessível no programa (eventualmente convertida para um dado tipo de dados).
O objeto System.in
representa um canal de leitura (em geral o teclado).
Como tal, podemos usá-lo como origem da informação, para que algum objeto Scanner
possa “consumir” os tokens que o utilizador escreve.
Fazemos isso quando criamos o objeto do tipo
Scanner
, indicando que o canal de leitura que ele vai usar é oSystem.in
:Scanner leitor = new Scanner (System.in);
Agora já podemos invocar métodos que lêem informação a partir do teclado tendo como alvo da invocação este objeto.
Por exemplo, o método String nextLine()
lê todos os carateres até encontrar um caráter especial que representa um “fim de linha” (gerado, por exemplo, quando o utilizador carrega na tecla Enter) e retorna a String
contendo esses carateres.
Outros exemplos são o método String next()
que lê a próxima token e retorna-a como uma String
, e o método int nextInt()
que lê a próxima token e retorna-a como um inteiro.
Por defeito, os delimitadores que definem até onde se estende uma token são o espaço, o tab ou o fim de linha.
As instruções seguintes:
xxxxxxxxxx
Scanner leitor = new Scanner (System.in);
System.out.println("Qual o seu nome?");
String nome = leitor.nextLine();
System.out.println("Viva " + nome + "!");
System.out.println("E que idade tem?");
int idade = leitor.nextInt();
if (idade > 150) {
System.out.println("Impossivel!");
}
produzem o seguinte efeito no ecrã caso o utilizador introduza os valores Pai Natal e 524:
xxxxxxxxxx
Qual o seu nome?
Pai Natal
Viva Pai Natal!
E que idade tem?
524
Impossivel!
Repare que o input do utilizador, embora escrito no teclado, é reproduzido pelo sistema operativo no ecrã.
Suponha que queremos garantir que o utilizador introduz um valor entre 1 e 20. Se isso for importante, não basta dizer isso ao utilizador. Ele pode, mesmo sem querer, escrever um valor não aceitável.
É muito importante que o programa consiga recusar valores e repetir o pedido enquanto o utilizador não introduz um valor aceitável.
O programa seguinte pede uma nota entre 1 e 20 ao utilizador, só aceitando um valor que satisfaça essa condição.
ximport java.util.Scanner;
public class NotasInput {
public static void main (String [] args) {
Scanner leitor = new Scanner (System.in);
System.out.println("Qual a nota? (valor entre 1 e 20)");
double nota;
boolean erro;
do {
nota = leitor.nextDouble();
erro = nota < 1 || nota > 20;
if ( erro ){
System.out.println ("Tem que ser um valor entre 1 e 20!");
}
} while ( erro );
System.out.println("Nota arredondada: " + Math.round(nota));
}
}
Exemplo de execução deste programa:
xxxxxxxxxx
Qual a nota? (valor entre 1 e 20)
-3.4
Tem que ser um valor entre 1 e 20!
25.4
Tem que ser um valor entre 1 e 20!
13.6
Nota arredondada: 14
Os métodos int nextInt()
e double nextDouble()
, que lêem a próxima token acessível no canal de leitura sobre o qual o Scanner
trabalha, provocam um erro de execução no caso de a token lida não constituir um valor numérico do tipo correspondente.
Os valores que estes métodos conseguem manipular são
nextInt()
nextDouble()
. Quando a próxima token no canal de leitura não tem estas características (por exemplo, porque contém uma letra ou carácter especial) o erro consequente é representado por uma exceção do tipo InputMismatchException
.
Considere o seguinte pedaço de um programa:
xxxxxxxxxx
Scanner leitor = new Scanner(System.in);
System.out.println("Escreva um inteiro");
int i = leitor.nextInt();
System.out.println("Escreva um numero com parte decimal");
double x = leitor.nextDouble();
System.out.println("Produto dos dois: " + i * x);
No caso em que o utilizador insere, por exemplo, 2 e 1.5, o efeito destas instruções é:
xxxxxxxxxx
Escreva um inteiro
2
Escreva um numero com parte decimal
1.5
Produto dos dois: 3.0
Quando o utilizador insere, por exemplo, 1q3 em vez de um inteiro, o efeito é:
Escreva um inteiro
1q3
Exception in thread "main" java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:840)
at java.util.Scanner.next(Scanner.java:1461)
at java.util.Scanner.nextInt(Scanner.java:2091)
at java.util.Scanner.nextInt(Scanner.java:2050)
at Exemplo86.main(Exemplo86.java:63)
O mesmo aconteceria se o utilizador introduzisse qualquer caráter que não fosse um algarismo ou um ponto, quando lhe fosse pedido um número com parte decimal (através do método nextDouble()
).
Se quisermos tornar o nosso programa robusto no sentido de não “rebentar” no caso de o utilizador não introduzir um número inteiro quando o programa quer consumir um número inteiro, podemos testar cada token que é lida do canal de leitura e só o aceitar se for realmente um inteiro.
Para isso usamos o método hasNextInt()
que “espreita” para o canal de leitura e retorna true
se a próximo token for um número inteiro e false
caso contrário.
Conforme o resultado deste método (true ou false), podemos escolher
nextInt()
e usando o seu resultado ou next()
, que lê qualquer sequência de carateres que constitui uma token, desprezando o seu resultado.xxxxxxxxxx
import java.util.Scanner;
public class LeituraRobusta {
public static void main (String [] args) {
Scanner leitor = new Scanner (System.in);
System.out.println("Introduza um valor inteiro");
int valor = 0;
boolean erro = true;
do {
if (leitor.hasNextInt () ) { // espreita a proxima token
valor = leitor.nextInt (); // consome o inteiro
erro = false;
} else {
leitor.next (); // consome o que lah esteja
System.out.println ("Tem que ser um inteiro!");
}
} while ( erro );
System.out.println("O valor que introduziu eh: " + valor);
}
}
Exemplo de execução deste programa:
xxxxxxxxxx
Introduza um valor inteiro
1q3
Tem que ser um inteiro!
1.3
Tem que ser um inteiro!
123
O valor que introduziu eh: 123
Os seguintes métodos podem ser usados para fazer leitura robusta de inteiros num dado intervalo:
xxxxxxxxxx
/**
* Primeiro inteiro no canal de leitura que esta num dado intervalo
* @param infLim - Limite inferior do intervalo
* @param supLim - Limite superior do intervalo
* @param errMess - Mensagem de erro a apresentar no System.out
* @param sc - Canal de leitura
* @return um valor entre infLim e supLim
* @requires sc != null && infLim <= supLim && errMess != null
*/
static int lerValorNoIntervalo(int infLim, int supLim,
String errMess, Scanner sc) {
int valor = 0;
boolean erro;
do {
valor = lerInteiro ( errMess, sc );
erro = valor < infLim || valor > supLim;
if ( erro ){
System.out.println ( errMess );
}
} while ( erro );
return valor;
}
/**
* Primeiro inteiro no canal de leitura
* @param errMess - mensagem a escrever no System.out caso o valor
* acessivel no canal de leitura nao seja um inteiro
* @param sc - canal de leitura
* @return valor inteiro
* @requires errMess != null && sc != null
*/
private static int lerInteiro ( String errMess, Scanner sc ) {
int valor = 0;
boolean erro = true;
do {
if ( sc.hasNextInt () ) {
valor = sc.nextInt (); // consome o inteiro
erro = false;
}
else {
sc.next (); // consome o que lah esteja
System.out.println ( errMess );
}
} while ( erro );
return valor;
}
A seguinte instrução poderia então ser escrita num programa que tivesse acesso a estes métodos, para receber do utilizador um valor inteiro entre 1 e 20:
xxxxxxxxxx
Scanner leitor = new Scanner (System.in);
System.out.println("Introduza um inteiro entre 1 e 20");
int nota = lerValorNoIntervalo(1,
20,
"Tem que ser um inteiro entre 1 e 20!",
leitor);
Anterior: 8.4. A classe StringBuilder
Seguinte: 8.6. Aliasing