CodeGym /Cursos /JAVA 25 SELF /Leitura de arquivos de texto: linha a linha e por inteiro...

Leitura de arquivos de texto: linha a linha e por inteiro

JAVA 25 SELF
Nível 36 , Lição 1
Disponível

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é 1020 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
BufferedReader.readLine()
Arquivos grandes, processamento linha a linha Economiza memória, flexível Leitura apenas linha a linha
Files.readAllLines()
Arquivos pequenos e médios Lista de linhas imediatamente, simples Para arquivos grandes — OutOfMemory
Files.readString()
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.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION