W3docs

Come leggere un file riga per riga in Java

Leggi un file Java riga per riga con BufferedReader, Files.lines, Files.readAllLines e Scanner.

Leggere un file di testo una riga alla volta è una delle operazioni su file più comuni in Java. Il JDK offre diversi modi per farlo; la scelta giusta dipende dalla dimensione del file e dal fatto che si preferisca un semplice ciclo o una pipeline a stream. Questo capitolo mostra i quattro approcci idiomatici e quando usare ciascuno.

BufferedReader: il cavallo di battaglia per lo streaming

BufferedReader.readLine() legge una singola riga per chiamata e restituisce null alla fine del file, quindi si abbina naturalmente a un ciclo while. Racchiudilo in try-with-resources così il reader sottostante si chiude da solo:

Path file = Path.of("notes.txt");
try (BufferedReader br = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
  String line;
  while ((line = br.readLine()) != null) {
    System.out.println(line);
  }
}

Questo legge il file in streaming: solo una riga è tenuta in memoria per volta, quindi gestisce log di diversi gigabyte senza problemi. Files.newBufferedReader usa UTF-8 per impostazione predefinita, ma passare esplicitamente il charset documenta l'intenzione ed evita la decodifica dipendente dalla piattaforma. Nota che readLine() rimuove il terminatore di riga (\n, \r, o \r\n), quindi non lo vedrai mai nella stringa restituita.

La forma try (...) sopra è try-with-resources: garantisce la chiusura del reader anche se il ciclo lancia un'eccezione. Vedi stream bufferizzati per capire perché racchiudere un reader grezzo in un BufferedReader è importante per le prestazioni.

Files.lines: lo stesso lavoro come Stream

Quando vuoi una pipeline funzionale — filter, map, count — Files.lines ti fornisce uno Stream<String> pigro sulle righe del file:

try (Stream<String> lines = Files.lines(Path.of("notes.txt"), StandardCharsets.UTF_8)) {
  lines.filter(s -> !s.isBlank())
       .map(String::trim)
       .forEach(System.out::println);
}

Come BufferedReader, legge in modo pigro e non carica mai l'intero file. Il problema è che lo stream mantiene un file handle aperto, quindi deve essere chiuso — usalo sempre dentro try-with-resources, mai come espressione nuda. Dimenticarsi di farlo provoca una perdita di descrittori.

Files.readAllLines: file piccoli, tutto in una volta

Se il file è piccolo e vuoi tutte le righe in una List<String> fin dall'inizio, Files.readAllLines è l'opzione più diretta:

List<String> all = Files.readAllLines(Path.of("notes.txt"), StandardCharsets.UTF_8);
for (String line : all) {
  System.out.println(line);
}

È eagerly: l'intero file viene decodificato in memoria prima che tu tocchi la prima riga. Questo è comodo per file di configurazione e fixture, ma non adatto a file grandi — per quelli preferisci gli approcci in streaming. Scanner con nextLine() e hasNextLine() è una quarta opzione, utile quando devi anche analizzare token, ma è più lento e facile da usare in modo errato, quindi i tre sopra coprono la maggior parte dei casi. Per una visione più ampia dell'I/O su file — apertura, lettura di file interi e l'API NIO Files — vedi leggere file in Java.

ApproccioMemoriaRestituisceIdeale per
BufferedReader.readLine()Una rigaCiclo sempliceFile grandi, controllo manuale
Files.lines()Una riga (pigro)Stream<String>Pipeline su file grandi
Files.readAllLines()Intero fileList<String>File piccoli, accesso casuale
Scanner.nextLine()Una rigaCiclo sempliceParsing misto di righe e token

Esempio pratico: tutti e tre a confronto

Questo programma scrive un piccolo file temporaneo (così è autonomo), poi lo legge in tre modi — un ciclo con BufferedReader, uno stream con Files.lines, e un Files.readAllLines eager:

java— editable, runs on the server

Cosa imparare dall'esecuzione:

  • Il ciclo con BufferedReader numera quattro righe, e la riga 3: [] mostra che una riga vuota nel file viene restituita come stringa vuota, non saltata — readLine() riporta ogni riga, incluse quelle vuote.
  • readLine() ha stampato ogni riga senza alcun \n finale, confermando che rimuove il terminatore di riga; le uniche parentesi intorno al testo sono le lettere [ e ] aggiunte dal codice.
  • Files.lines ha contato non-blank lines: 3 perché filter(s -> !s.isBlank()) ha scartato la riga vuota — la pipeline a stream opera in modo pigro sulle stesse quattro righe viste dal ciclo.
  • Files.readAllLines ha riportato total lines: 4 e first line : alpha, dimostrando che ha caricato l'intero file eagerly in una List<String> indicizzabile con get(0).
  • Ogni reader si trovava dentro try-with-resources (o restituiva una List gestita), quindi il file handle e il file temporaneo sono stati rilasciati correttamente prima che venisse stampato done — nessun descrittore perso.

Esercitazione

Pratica
Perché uno stream restituito da Files.lines() deve essere usato dentro un blocco try-with-resources?
Perché uno stream restituito da Files.lines() deve essere usato dentro un blocco try-with-resources?
Was this page helpful?