flowchart LR
T["Teclat"] -->|flux d'entrada| P["El teu<br/>programa"]
F1["Fitxer"] -->|flux d'entrada| P
P -->|flux de sortida| PA["Pantalla"]
P -->|flux de sortida| F2["Fitxer"]
10 Entrada, sortida i fitxers
Objectius
- Entendre el concepte de flux (stream) de dades i la diferència entre fluxos de bytes i de caràcters.
- Llegir dades de teclat amb
Scannerde manera robusta. - Formatar la sortida amb
printfiDecimalFormat. - Representar rutes amb
FileiPath. - Llegir i escriure fitxers de text.
- Crear, esborrar i comprovar l’existència de fitxers i directoris.
- Tancar recursos automàticament amb el try-with-resources.
Fins ara els nostres programes tot passava en memòria i en acabar s’esborrava. Per fer coses útils, un programa ha de comunicar-se amb l’exterior: llegir el que escriu l’usuari, mostrar resultats amb bon format i, sobretot, guardar informació que sobrevisqui al tancament del programa. Això últim és el que fan els fitxers.
10.1 El concepte de flux o stream
Java modela tota entrada i sortida de dades com un flux: un corrent de dades que va d’un origen a un destí, peça a peça i en ordre.
Un flux és com una canonada. Per un extrem entra l’aigua (les dades) i per l’altre surt. No veus tota l’aigua alhora: passa gota a gota, en ordre.
Un flux d’entrada porta dades cap al programa (del teclat, d’un fitxer); un flux de sortida les treu del programa (a la pantalla, a un fitxer).
Bytes o caràcters
Hi ha dues grans famílies de fluxos, segons la unitat de dades que transporten:
| Concepte | Flux de bytes | Flux de caràcters |
|---|---|---|
| Unitat | Bbyte (8 bits) | caràcter (lletra, respecta accents) |
| Per a què | Dades binàries: imatges, àudio, vídeo, PDF | Text: .txt, .csv, codi font |
| Classes base | InputStream / OutputStream |
Reader / Writer |
| Exemple | FileInputStream |
FileReader, BufferedReader |
Si obres el fitxer amb un editor de text i l’entens (lletres, números), és text → fes servir fluxos de caràcters.
Si l’obres i veus símbols estranys (una imatge, una cançó), és binari → fluxos de bytes.
En aquest capítol treballarem sobretot amb text.
Els accents i la ce trencada són un motiu important per triar bé: els fluxos de caràcters saben interpretar la codificació (com UTF-8) i respecten la ç, la à o la ï. Els de bytes veuen només nombres.
10.2 Entrada per teclat amb Scanner
Ja vam utilitzar la classe Scanner en la introducció a Java. És la forma més còmoda per llegir el que escriu l’usuari. Recorda que primer cal importar-lo.
Es connecta al flux d’entrada estàndard (el teclat), que és System.in:
import java.util.Scanner;
...
Scanner sc = new Scanner(System.in);
System.out.print("Com et dius? ");
String nom = sc.nextLine();
System.out.print("Quants anys tens? ");
int edat = sc.nextInt();
System.out.println("Hola " + nom + ", tens " + edat + " anys.");Els mètodes més útils de Scanner:
| Mètode | Llegeix |
|---|---|
nextLine() |
Tota una línia de text (fins a l’Intro) |
next() |
Una sola paraula |
nextInt() |
Un enter |
nextDouble() |
Un decimal |
nextBoolean() |
true o false |
nextInt() seguit de nextLine()
Quan fas nextInt(), Scanner llegeix el nombre però deixa el salt de línia pendent.
Si just després fas nextLine(), aquest llegeix aquell salt buit i sembla que “s’ha saltat” la pregunta:
int edat = sc.nextInt();
System.out.print("Ciutat? ");
String ciutat = sc.nextLine(); // Llegeix "" i no espera l'usuari.Solució: afegeix un sc.nextLine() extra després del nextInt() per consumir el salt pendent.
10.3 Formatar la sortida
System.out.println va bé, però quan volem controlar exactament com es veuen els nombres (decimals, alineació, símbol de moneda) ens queden curts.
Java ens ofereix les eines:
- Mètode
printfdeSystem.out. - Classe
DecimalFormat.
El mètode printf (print formatted) imprimeix segons una plantilla amb marcadors que comencen per %:
double preu = 19.5;
int unitats = 3;
System.out.printf("%d unitats a %.2f € = %.2f €%n", unitats, preu, unitats * preu);
// Sortida: 3 unitats a 19,50 € = 58,50 €Els marcadors més habituals són:
| Marcador | Significa | Exemple |
|---|---|---|
%d |
Enter | 42 |
%f |
Decimal | 3.140000 |
%.2f |
Decimal amb 2 xifres | 3.14 |
%s |
Text (String) |
hola |
%n |
Salt de línia | |
%5d |
Enter en 5 espais (alineat a la dreta) | 42 |
%n i no \n?
%n produeix el salt de línia correcte del sistema operatiu on s’executa el programa (Windows i Linux fan servir salts diferents).
Dins de printf és preferible a \n.
La classe DecimalFormat en permet un control més afinat del format d’un nombre: separador de milers, decimals fixos, símbols,..
import java.text.DecimalFormat;
DecimalFormat df = new DecimalFormat("#,##0.00");
System.out.println(df.format(1234567.5)); // 1.234.567,50Els patrons es construeixen amb símbols: 0 (xifra obligatòria), # (xifra opcional), , (separador de milers), . (separador decimal).
Mostra el resultat de dividir 10 entre 3 amb exactament 4 decimals, primer amb printf i després amb DecimalFormat. Compara els dos resultats.
10.4 Rutes: File i Path
Per treballar amb fitxers, primer hem de saber on són. Una ruta és l’adreça d’un fitxer dins del sistema d’arxius. Java ofereix dues maneres de representar-les:
File(paquetjava.io): la manera clàssica, encara molt usada.Path(paquetjava.nio.file): la manera moderna, més potent i recomanada.
import java.io.File;
import java.nio.file.Path;
File fitxerVell = new File("dades/socis.txt");
Path fitxerNou = Path.of("dades/socis.txt");A Windows les rutes fan servir \ (C:\Users\...), però a Java la barra invertida dins d’un String és un caràcter especial.
Si escrius "C:\nova", el \n es converteix en un salt de línia. Per solucionar-ho:
- Fes servir barres normals
/, que funcionen a tots els sistemes:"C:/Users/dades.txt". - O bé duplica la barra:
"C:\\Users\\dades.txt".
La recomanació és treballar amb rutes relatives ("dades/socis.txt") sempre que puguis.
Amb File pots consultar informació d’un fitxer sense ni tan sols obrir-lo:
File f = new File("dades/socis.txt");
System.out.println("Existeix? " + f.exists());
System.out.println("És un directori? " + f.isDirectory());
System.out.println("Mida en bytes: " + f.length());10.5 Llegir un fitxer de text
Per llegir text, la combinació més còmoda és BufferedReader i FileReader. Llegeix línia a línia i de manera eficient.
Com que treballem amb fitxers, hem de gestionar la IOException.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Llegir {
public static void main(String[] args) {
try (BufferedReader br = new BufferedReader(new FileReader("dades/socis.txt"))) {
String linia;
while ((linia = br.readLine()) != null) { // null = final del fitxer
System.out.println(linia);
}
} catch (IOException e) {
System.out.println("No s'ha pogut llegir el fitxer: " + e.getMessage());
}
}
}El bucle while ((linia = br.readLine()) != null) és un patró que veuràs molt: readLine() retorna la línia següent, o null quan ja no en queden.
Així recorrem el fitxer sencer sense saber per endavant quantes línies té.
Has vist el try (...) amb parèntesis? Es diu try-with-resources.
Qualsevol recurs que obris entre aquests parèntesis (com el BufferedReader) es tanca automàticament en acabar, tant si hi ha hagut error com si no.
És la manera moderna i segura de treballar amb fitxers: t’estalvia el finally per tancar-los a mà i evita fuites de recursos.
Una alternativa encara més curta, quan el fitxer no és enorme, és llegir-lo tot de cop amb Files.readAllLines, que et retorna una llista de línies:
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
List<String> linies = Files.readAllLines(Path.of("dades/socis.txt"));
for (String l : linies) {
System.out.println(l);
}10.6 Escriure un fitxer de text
Per escriure fem servir BufferedWriter (o el pràctic PrintWriter, que té println i printf com System.out):
import java.io.PrintWriter;
import java.io.FileWriter;
import java.io.IOException;
public class Escriure {
public static void main(String[] args) {
try (PrintWriter pw = new PrintWriter(new FileWriter("dades/sortida.txt"))) {
pw.println("Anna Puig;25");
pw.println("Bru Camps;31");
pw.printf("Total socis: %d%n", 2);
} catch (IOException e) {
System.out.println("No s'ha pogut escriure: " + e.getMessage());
}
}
}Per defecte, new FileWriter("fitxer.txt") esborra tot el contingut anterior del fitxer i comença de zero.
Si el que vols és afegir al final sense perdre el que hi havia, passa true com a segon argument:
new FileWriter("fitxer.txt", true) // Mode APPEND: afegeix al finalMolta gent ha perdut dades per no adonar-se d’aquest detall.
Aquest és el cicle de vida complet de treballar amb un fitxer:
flowchart LR
A["Obrir<br/>(FileReader/Writer)"] --> B["Llegir o<br/>escriure"]
B --> C["Tancar<br/>(automàtic amb<br/>try-with-resources)"]
Escriu un programa que demani noms per teclat (fins que l’usuari escrigui “fi”) i els vagi guardant, un per línia, en un fitxer noms.txt.
Després, torna a obrir el fitxer i mostra tots els noms per pantalla.
10.7 Crear i esborrar fitxers i directoris
Sovint cal preparar l’estructura de carpetes abans de guardar-hi res.
La classe Files ens ho facilita:
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;
Path carpeta = Path.of("dades");
Path fitxer = Path.of("dades/registre.txt");
try {
if (!Files.exists(carpeta)) {
Files.createDirectories(carpeta); // Crea la carpeta (i les intermèdies)
}
if (!Files.exists(fitxer)) {
Files.createFile(fitxer); // Crea el fitxer buit
}
Files.delete(fitxer); // Esborra el fitxer
} catch (IOException e) {
System.out.println("Error d'operació de fitxers: " + e.getMessage());
}Operacions més habituals amb Files:
| Operació | Mètode |
|---|---|
| Comprovar si existeix | Files.exists(ruta) |
| Crear un fitxer buit | Files.createFile(ruta) |
| Crear un directori (i pares) | Files.createDirectories(ruta) |
| Esborrar | Files.delete(ruta) |
| Copiar | Files.copy(origen, desti) |
| Moure o reanomenar | Files.move(origen, desti) |
Files.delete sobre un directori només funciona si està buit. Si conté fitxers, llançarà una excepció. Primer has de buidar-lo.
Esborrar és definitiu, no va a la paperera de reciclatge. Comprova sempre la ruta abans.
Escriu un programa que comprovi si existeix la carpeta sortides/; si no existeix, que la creï; i que després hi generi un fitxer informe.txt amb la data d’avui.
10.8 Resum
- Java modela l’entrada/sortida com a fluxos o streams: corrents de dades ordenats entre un origen i un destí.
- Hi ha fluxos de bytes (dades binàries: imatges, àudio) i de caràcters (text, respecta els accents).
Scannerllegeix del teclat; vigila el problema denextInt()seguit denextLine().printfiDecimalFormatdonen control sobre el format de la sortida.File(clàssic) iPath(modern) representen rutes; a Windows, usa/o\\.- Per llegir text:
BufferedReader+readLine()en bucle, oFiles.readAllLines. - Per escriure text:
PrintWriter/BufferedWriter; el segon argumenttruedeFileWriterafegeix en comptes de sobreescriure. - El try-with-resources (
try (...)) tanca els recursos automàticament: fes-lo servir sempre amb fitxers. - La classe
Filespermet crear, esborrar, copiar i moure fitxers i directoris.
10.9 Per practicar
- Agenda simple. Demana noms i telèfons per teclat i guarda’ls en un fitxer
agenda.csvamb el formatnom;telefon. - Comptador de línies. Llegeix un fitxer de text i mostra quantes línies, quantes paraules i quants caràcters té.
- Factura formatada. Demana producte, preu i unitats, i escriu en un fitxer una línia ben alineada amb
printf(imports amb 2 decimals i símbol €). - Registre d’accessos. Cada cop que s’executi el programa, afegeix (mode append) una línia amb la data actual a
log.txt. - Còpia de seguretat. Fes un programa que copiï el contingut d’un fitxer a un altre línia a línia.
- Gestor de carpetes. Crea l’estructura
projecte/dades/iprojecte/sortides/si no existeix, i informa de què ha calgut crear. - Mitjana des de fitxer. Llegeix un fitxer amb un nombre per línia i calcula’n la mitjana, gestionant amb
try-catchles línies que no siguin nombres (Capítol 8).