1. Leitura linha a linha: BufferedReader e companhia
Já vimos a leitura byte a byte: isso é útil para formatos binários, mas para texto esse método é incômodo. Um arquivo é composto por caracteres que dependem da codificação. Por isso, o Java oferece “abstrações” — FileReader, BufferedReader e outras classes — que convertem um fluxo de bytes em um fluxo de caracteres e linhas.
Imagine um arquivo de texto — seja um log de execução do programa, uma lista de usuários ou até o romance enorme “Guerra e Paz”. Às vezes é preciso ler rapidamente o arquivo inteiro, às vezes — percorrê-lo linha a linha, e às vezes — extrair uma linha específica.
No Java há várias maneiras de fazer isso, e a escolha depende do tamanho do arquivo e da tarefa. Se você precisa processar o arquivo linha por linha (por exemplo, contar o número de linhas ou encontrar um registro), use a leitura linha a linha. E se o arquivo for pequeno — você pode carregá-lo todo na memória e trabalhar com ele como uma lista de linhas.
Por que ler linha a linha é bom?
Para arquivos grandes, a leitura linha a linha evita problemas de memória. Carregar um log de gigabytes na memória é uma péssima ideia: há grande chance de topar com OutOfMemoryError. Já ler o arquivo linha por linha pode ser feito sem grandes custos, mesmo que ele tenha centenas de megabytes.
Como fazer isso em Java?
A maneira mais clássica é usar BufferedReader (ou classes relacionadas) e o método readLine().
import java.io.*;
public class ReadLinesDemo {
public static void main(String[] args) {
String fileName = "example.txt";
try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
String line;
int lineNumber = 1;
while ((line = reader.readLine()) != null) {
System.out.printf("%3d: %s%n", lineNumber, line);
lineNumber++;
}
} catch (IOException e) {
System.out.println("Erro ao ler o arquivo: " + e.getMessage());
}
}
}
Aqui abrimos o arquivo para leitura e, com readLine(), lemos uma linha por vez até chegar ao fim (null). Depois, imprimimos cada linha com seu número.
Resumo sobre try-with-resources
Viu a construção try (...) { ... }? Isso é o try-with-resources. Ele garante que o arquivo será fechado, mesmo se ocorrer um erro no meio da leitura. Você não precisa chamar close() manualmente: o fechamento ocorre automaticamente, inclusive com catch/finally.
Por que usar BufferedReader?
BufferedReader não lê caractere por caractere, mas em blocos (normalmente de 8192 bytes), o que acelera o trabalho com arquivos. Além disso, ele tem o método conveniente readLine(), que retorna a linha imediatamente até o caractere de quebra de linha.
Qual buffer escolher?
Normalmente, BufferedReader usa um buffer de 8192 bytes (8 KB) — isso é suficiente para a maioria das tarefas. Se você lê linhas muito longas (por exemplo, de 100_000 caracteres), o buffer pode ser aumentado:
BufferedReader reader = new BufferedReader(new FileReader(fileName), 65536); // buffer de 64 KB
Mas, para tarefas comuns, o tamanho padrão é perfeito.
2. Leitura do arquivo inteiro: Files.readAllLines e Files.readString
Se o arquivo for pequeno (por exemplo, até 10–20 MB), é conveniente carregá-lo por inteiro. Por exemplo, quando você precisa obter rapidamente a lista de todas as linhas, analisar o texto ou enviá-lo pela rede.
Abordagem moderna: Files.readAllLines
Desde o Java 7 existe a classe Files com um monte de métodos úteis.
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
public class ReadAllLinesDemo {
public static void main(String[] args) {
Path path = Path.of("example.txt");
try {
List<String> lines = Files.readAllLines(path);
for (int i = 0; i < lines.size(); i++) {
System.out.printf("%3d: %s%n", i + 1, lines.get(i));
}
} catch (IOException e) {
System.out.println("Erro ao ler o arquivo: " + e.getMessage());
}
}
}
Aqui o método Files.readAllLines(path) retorna uma lista de linhas (List<String>). Com essa lista você pode trabalhar como com uma coleção comum: buscar, ordenar, filtrar.
Ainda mais moderno: Files.readString (Java 11+)
Se você precisa obter o arquivo inteiro em uma única String (por exemplo, para procurar uma substring ou enviar em JSON), use Files.readString:
import java.nio.file.*;
import java.io.IOException;
public class ReadStringDemo {
public static void main(String[] args) {
Path path = Path.of("example.txt");
try {
String content = Files.readString(path);
System.out.println("Conteúdo do arquivo:");
System.out.println(content);
} catch (IOException e) {
System.out.println("Erro ao ler o arquivo: " + e.getMessage());
}
}
}
E a codificação?
Por padrão, os métodos Files.readAllLines e Files.readString usam a codificação padrão do seu sistema. Se o arquivo estiver em outra codificação (por exemplo, Windows-1251), informe-a explicitamente:
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
String content = Files.readString(path, StandardCharsets.UTF_8);
3. Comparação das abordagens: quando usar qual
| Método | Quando usar | Vantagens | Desvantagens |
|---|---|---|---|
|
Arquivos grandes, processamento linha a linha | Economiza memória, flexível | Leitura apenas linha a linha |
|
Arquivos pequenos e médios | Lista de linhas imediatamente, simples | Para arquivos grandes — OutOfMemory |
|
Arquivos pequenos, texto completo necessário | Texto inteiro em uma única String | Sem divisão em linhas |
Recomendação:
— Se o arquivo for pequeno — use Files.readAllLines ou Files.readString.
— Se o arquivo for grande ou você não souber o tamanho — use BufferedReader.readLine().
4. Tarefas práticas: exemplos e análise
Exemplo 1. Contagem de linhas em um arquivo grande
Suponha que você precise saber quantas linhas há em um arquivo enorme (por exemplo, um log de servidor).
import java.io.*;
public class LineCount {
public static void main(String[] args) {
String fileName = "biglog.txt";
int lineCount = 0;
try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
while (reader.readLine() != null) {
lineCount++;
}
System.out.println("Total de linhas no arquivo: " + lineCount);
} catch (IOException e) {
System.out.println("Erro ao ler o arquivo: " + e.getMessage());
}
}
}
Exemplo 2. Busca de linhas por conteúdo
Vamos procurar todas as linhas onde apareça a palavra “erro” (sem diferenciar maiúsculas/minúsculas):
import java.io.*;
public class FindErrorLines {
public static void main(String[] args) {
String fileName = "biglog.txt";
String keyword = "erro";
try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
String line;
int lineNumber = 1;
while ((line = reader.readLine()) != null) {
if (line.toLowerCase().contains(keyword)) {
System.out.printf("%3d: %s%n", lineNumber, line);
}
lineNumber++;
}
} catch (IOException e) {
System.out.println("Erro ao ler o arquivo: " + e.getMessage());
}
}
}
Exemplo 3. Carregar configuração de um arquivo pequeno
Arquivo config.txt:
host=localhost
port=8080
mode=dev
Lemos o arquivo inteiro e o dividimos em pares chave-valor:
import java.nio.file.*;
import java.util.*;
public class ConfigLoader {
public static void main(String[] args) throws Exception {
Path path = Path.of("config.txt");
List<String> lines = Files.readAllLines(path);
Map<String, String> config = new HashMap<>();
for (String line : lines) {
if (line.trim().isEmpty() || line.startsWith("#")) continue; // pular linhas vazias e comentários
String[] parts = line.split("=", 2);
if (parts.length == 2) {
config.put(parts[0].trim(), parts[1].trim());
}
}
System.out.println("Configuração carregada: " + config);
}
}
5. Erros comuns ao ler arquivos de texto
Erro nº 1: tentar ler um arquivo binário como texto. Se você abrir uma imagem ou um arquivo compactado com BufferedReader ou Files.readAllLines, verá caracteres sem sentido e corre o risco de OutOfMemoryError. Para arquivos binários, use InputStream!
Erro nº 2: não tratar exceções. Arquivos podem ser excluídos, movidos ou bloqueados. Sempre envolva a leitura em try-catch e informe o usuário sobre problemas.
Erro nº 3: ignorar a codificação. Se você tiver texto em russo e ler o arquivo sem especificar a codificação, pode obter “?????”. Use StandardCharsets.UTF_8 ou a codificação de que precisa.
Erro nº 4: esquecer de fechar o stream. Se não fechar o arquivo, ele pode permanecer bloqueado até o término do programa. Sempre use try-with-resources.
Erro nº 5: usar read() em vez de readLine() para arquivos de texto. O método read() lê caractere por caractere — isso é lento e inconveniente para linhas. Para texto, use readLine() ou os métodos da classe Files.
GO TO FULL VERSION