Escanejar variables d'entrada

En aquesta activitat aprendràs a fer programes interactius que demanen dades i després les utilitzen.

Aprendràs:

Com obtenir dades de l'entrada estàndard i guardar-les en variables

L'entrada estàndard i la classe Scanner

L'entrada estàndard és un flux de dades que van cap al programa. Està suportada pel sistema operatiu. Per defecte, l'entrada estàndard obté les dades del teclat, tot i que és possible redirigir-la per a obtenir les dades d'un fitxer.

De fet, no tots els programes utilitzen l'entrada estàndard. Però nosaltres l'utilitzarem molt sovint per a demanar dades als usuaris per teclat, o per a obtenir les dades dels casos de prova en els exercicis.

La forma típica en que resoldrem problemes és aquesta:

  1. Llegir dades de l'entrada estàndard (System.in)

  2. Processar les dades per a obtenir un resultat

  3. Imprimir el resultat a la sortida estàndard (System.out)

Aquest tipus de problemes de programació es poden provar fàcilment amb diferents formats de dades d'entrada, i per aquest motiu, els utilitzarem molt.

Llegir dades amb un scanner

La forma més simple d'obtenir dades de l'entrada estàndard és utilitzar la classe Scanner. Permet als programes llegir valors de diferents tipus (strings, números, etc.) de l'entrada estàndard.

Per a utilitzar la classe Scanner s'ha d'afegir aquesta sentència d'importació al principi del fitxer de codi:

import java.util.Scanner;

Després s'ha d'afegir aquesta construcció a dintre del mètode main:

Scanner sc = new Scanner(System.in);

En aquesta línia hem creat un objecte de la classe Scanner, que ens permet utilitzar els seus mètodes. Aprendrem més sobre classes i objectes més endavant. System.in indica que es llegirà el flux de dades de l'entrada estàndard (habitualment, el teclat). De moment, no és necessari que comprenguis aquesta línia, solament escriu-la exactament així.

Anem a escriure el primer programa que llegeix dades del teclat:

Prova a escriure i executar aquest programa a l'IntelliJ.

import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("Hola, com et dius?"); String nom = sc.nextLine(); System.out.print("Hola "); System.out.print(nom); System.out.println("!"); } }

Quan l'executem, veurem el següent (en verd es mostra l'entrada introduïda per teclat):

Hola, com et dius? Josep Ramon Hola Josep Ramon!

Veiem pas a pas, l'execució del programa:

Mètodes de la classe Scanner

Hi ha diversos mètodes a la classe Scanner que ens permeten llegir diferents tipus de dades de l'entrada estàndard. En aquesta taula tens un resum dels mètodes que més utilitzarem en aquest curs:

MètodeTipusUtilitat
nextInt()intllegeix un número enter
nextFloat()floatllegeix un número decimal
nextBoolean()booleanllegeix un valor booleà
next()Stringllegeix una paraula
nextLine()Stringllegeix fins al final de línia

Quan llegim dades utilitzant aquests mètodes hem d'utilitzar les variables del tipus adequat per a guardar la dada que es llegeix. Per exemple:

import java.util.Scanner; public class Main { public static void main(String[] args){ Scanner sc = new Scanner(System.in); int quantitat = sc.nextInt(); float preu = sc.nextFloat(); boolean pagat = sc.nextBoolean(); String codi = sc.next(); String nom = sc.nextLine(); System.out.println(quantitat); System.out.println(preu); System.out.println(pagat); System.out.println(codi); System.out.println(nom); } } 5 1,99 true PR101 Galetes Maria 5 1.99 true PR101 Galetes Maria

Observa que el preu introduït per teclat s'ha escrit utilitzant una coma , en lloc d'un punt . per a separar les xifres decimals (1,99 en lloc de 1.99). Això es degut a que l'Scanner utilitza l'idioma del Sistema Operatiu per al format d'entrada, i en castellà/català els decimals es separen amb una coma ,. Si tinguèssim el Sistema Operatiu configurat en anglès, caldria haver introduït el número decimal utlitzant un punt.

Es pot forçar a l'Scanner a utilitzar l'idioma anglès per al format de dades d'entrada, és a dir, a utilitzar el punt en els decimals. Ho podem fer amb el mètode setLocale(), just després de crear l'scanner, així:

Scanner sc = new Scanner(System.in); sc.useLocale(Locale.US); // usar el format angles

Per contra, els mètodes print() i println() sempre imprimeixen separant els decimals amb un punt.

Hem vist que cal utilitzar el tipus de variable adequat al mètode que utilitzem per a llegir. Si no utilitzem el tipus adequat, el programa no s'executarà. Per exemple, no podem utilitzar una variable de tipus String per a guardar un valor llegit amb nextInt():

String a = sc.nextInt(); // error, cal utlitzar el tipus int

Per sort, l'IDE ens ajuda a detectar i solucionar aquests errors, indicant en subratllat vermell l'error i proporcionant ajuda sobre com solucionar-lo:

InputMismatchException

Una altre error que pot ocòrrer és que l'usuari no introdueixi una dada que correspongui amb el tipus que s'ha de llegir. En aquest cas, el programa donarà una excepció i s'aturarà inesperadament.

Per exemple, el següent programa tracta de llegir un enter del teclat, però l'usuari introdueix un text (holaaa). L'execució del programa s'abortarà amb una excepció de tipus InputMismatchException:

System.out.println("Introdueix un número:"); int numero = sc.nextInt(); Introdueix un número: holaaa Exception in thread "main" java.util.InputMismatchException at java.base/java.util.Scanner.throwFor(Scanner.java:939) at java.base/java.util.Scanner.next(Scanner.java:1594) at java.base/java.util.Scanner.nextInt(Scanner.java:2258)

Més endavant veurem com prevenir aquestes situacions, i fer el nostre programa resistent a qualsevol entrada que introdueixi l'usuari.

Consumir dades de l'entrada

Les crides als mètodes de lectura de l'scanner van consumint dades de l'entrada sempre que hi hagi, i si no hi han dades esperaran fins que hi hagin.

Vegem-ho amb un exemple. Aquest programa llegeix dos números de l'entrada:

System.out.println("Introdueix el primer número:") int a = sc.nextInt(); System.out.println("Introdueix el segon número:") int b = sc.nextInt(); System.out.println("Els números introduïts són:") System.out.println(a); System.out.println(b);

Quan executem el programa, la primera crida a nextInt() 2, quedarà esperant a que l'usuari introdueixi un número. Si l'usuari introdueix un únic número, aquest serà consumit de l'entrada i assignat a la variable a. Quan es fa la segona crida a nextInt() 4, el programa tornarà a esperar a que s'introdueixi un altre número.

Introdueix el primer número: 1234 Introdueix el segon número: 6789 Els números introduïts són: 1234 6789

Però, si a la primera crida a nextInt() 2, l'usuari introdueix dos números, el primer d'ells serà consumit per l'scanner i assignat a la variable a, i el segon número quedarà pendent per consumir. Així, a la segona crida a nextInt() 4, l'scanner no esperarà a que s'introdueixi un altre número, sinò que consumirà el número que havia quedat per consumir i quedarà assignat a la variable b sense esperar a que s'introdueixi un altre número.

Introdueix el primer número: 1234 6789 // introdueix dos numeros Introdueix el segon número: // consumeix el segon número Els números introduïts són: 1234 6789

Hi ha situacions en què és una mica enrevesada la lectura de dades. Cal tenir en compte la posició del cursor i la forma en que llegeixen les dades els mètodes.

El primer a tenir en compte és que tots els mètodes, excepte el nextLine(), descarten qualsevol espai-en-blanc o salt-de-línia que troben al flux de dades de l'entrada, fins que troben una dada per a llegir. Per contra, nextLine(), llegirà tots els caracters (incloent espais-en-blanc) fins que trobi un salt-de-línia (que descartarà).

Vejem els comportaments d'aquests mètodes amb varis exemples. Al flux de dades d'entrada indicarem els espais-en-blanc amb el símbol i els salts-de-línia amb el símbol .

Fes coincidir els mètodes de la classe Scanner amb el tipus de valor que llegeixen.

nextInt()int nextBoolean()boolean nextFloat()float next()String

Invoquem el mètode next() i introduïm el número 9 a l'entrada estàndard, quin tipus de dada serà?

String char int float

Hello you
Cada paraula a una línia
Contractar un xef
Carta formal
Arxius de codi font