W3docs

Como Ler um Arquivo Linha por Linha em Java

Leia um arquivo Java linha por linha com BufferedReader, Files.lines, Files.readAllLines e Scanner.

Ler um arquivo de texto uma linha por vez é uma das tarefas de arquivo mais comuns em Java. O JDK oferece várias formas de fazer isso; a escolha certa depende do tamanho do arquivo e se você prefere um loop simples ou um pipeline de stream. Este capítulo apresenta as quatro abordagens idiomáticas e quando usar cada uma.

BufferedReader: o motor de streaming

BufferedReader.readLine() lê uma única linha por chamada e retorna null no final do arquivo, combinando naturalmente com um loop while. Envolva-o em try-with-resources para que o reader subjacente se feche automaticamente:

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);
  }
}

Isso faz streaming do arquivo: apenas uma linha é mantida na memória por vez, de modo que ele lida com um log de vários gigabytes sem problemas. Files.newBufferedReader usa UTF-8 por padrão, mas passar o charset explicitamente documenta a intenção e evita decodificação dependente de plataforma. Note que readLine() remove o terminador de linha (\n, \r ou \r\n), por isso você nunca o vê na string retornada.

A forma try (...) acima é try-with-resources: ela garante que o reader seja fechado mesmo que o loop lance uma exceção. Veja buffered streams para entender por que envolver um reader bruto em um BufferedReader é importante para o desempenho.

Files.lines: o mesmo trabalho como um Stream

Quando você quer um pipeline funcional — filter, map, count — Files.lines fornece um Stream<String> lazy sobre as linhas do arquivo:

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);
}

Assim como BufferedReader, ele lê de forma lazy e nunca carrega o arquivo inteiro. O detalhe é que o stream mantém um file handle aberto, portanto ele deve ser fechado — sempre use-o dentro de try-with-resources, nunca como uma expressão solta. Esquecer isso causa vazamento de descritores.

Files.readAllLines: arquivos pequenos, tudo de uma vez

Se o arquivo é pequeno e você quer todas as linhas em uma List<String> imediatamente, Files.readAllLines é a opção mais direta:

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

É eager: o arquivo inteiro é decodificado para a memória antes que você acesse a primeira linha. Isso é conveniente para arquivos de configuração e fixtures, mas inadequado para arquivos grandes — prefira as abordagens de streaming nesses casos. Scanner com nextLine() e hasNextLine() é uma quarta opção, útil quando você também precisa analisar tokens, mas é mais lento e fácil de usar incorretamente, então os três acima cobrem a maioria dos casos. Para uma visão mais ampla de I/O de arquivos — abrir, ler arquivos inteiros e a API NIO Files — veja reading files in Java.

AbordagemMemóriaRetornaMelhor para
BufferedReader.readLine()Uma linhaLoop simplesArquivos grandes, controle manual
Files.lines()Uma linha (lazy)Stream<String>Pipelines em arquivos grandes
Files.readAllLines()Arquivo inteiroList<String>Arquivos pequenos, acesso aleatório
Scanner.nextLine()Uma linhaLoop simplesAnálise mista de linha + token

Exemplo prático: os três lado a lado

Este programa escreve um pequeno arquivo temporário (para ser autocontido) e depois o lê de três formas — um loop com BufferedReader, um stream com Files.lines e um Files.readAllLines eager:

java— editable, runs on the server

O que observar na execução:

  • O loop com BufferedReader numera quatro linhas, e a linha 3: [] mostra que uma linha em branco no arquivo é retornada como uma string vazia, não ignorada — readLine() reporta cada linha, incluindo as vazias.
  • readLine() imprimiu cada linha sem nenhum \n no final, confirmando que ele remove o terminador de linha; os únicos colchetes ao redor do texto são os literais [ e ] adicionados pelo código.
  • Files.lines contou non-blank lines: 3 porque o filter(s -> !s.isBlank()) descartou a linha vazia — o pipeline de stream opera de forma lazy sobre as mesmas quatro linhas que o loop viu.
  • Files.readAllLines reportou total lines: 4 e first line : alpha, provando que ele carregou o arquivo inteiro de forma eager em uma List<String> que você pode indexar com get(0).
  • Cada reader estava dentro de try-with-resources (ou retornou uma List gerenciada), portanto o file handle e o arquivo temporário foram liberados corretamente antes de done ser impresso — sem vazamento de descritores.

Prática

Prática
Por que um stream retornado por Files.lines() deve ser usado dentro de um bloco try-with-resources?
Por que um stream retornado por Files.lines() deve ser usado dentro de um bloco try-with-resources?
Was this page helpful?