Leitura e Escrita de Arquivos de Texto em Java com FileReader e FileWriter
Enquanto streams de bytes (InputStream, OutputStream) são perfeitos para dados binários, eles não são a ferramenta ideal para manipular arquivos de texto. A razão é simples: arquivos de texto são compostos por caracteres, e a forma como esses caracteres são representados como bytes é definida por uma codificação (ou charset, como UTF-8).
Para lidar com essa abstração de forma correta, Java oferece as classes base Reader e Writer e suas implementações concretas, como FileReader e FileWriter.
Escrita de Arquivos com a Classe FileWriter
A classe FileWriter herda de Writer e é projetada para a escrita de caracteres em arquivos. Ela gerencia automaticamente a conversão de caracteres (char) para bytes, usando a codificação padrão da plataforma onde o código está sendo executado.
Seus construtores mais comuns permitem especificar o arquivo de destino e o modo de escrita:
FileWriter(String fileName)
FileWriter(String fileName, boolean append)
FileWriter(File file)
FileWriter(File file, boolean append)O parâmetro booleano append determina se os novos dados devem ser adicionados ao final do arquivo (true) ou se o arquivo deve ser sobrescrito (false).
O exemplo a seguir demonstra como gravar um texto em um arquivo:
import java.io.*;
public class Program {
public static void main(String[] args) {
try (FileWriter writer = new FileWriter("notes.txt", false)) {
// Grava a string inteira no arquivo
String text = "Hello Gold!";
writer.write(text);
// Grava caracteres individualmente
writer.append('\n');
writer.append('E');
writer.flush(); // Garante que os dados em buffer sejam gravados
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}Neste código, um FileWriter é criado para o arquivo notes.txt. Os métodos write() e append() são usados para enviar os caracteres para o stream, que os converte em bytes e os grava no arquivo.
Leitura de Arquivos com a Classe FileReader
A classe FileReader, que herda de Reader, fornece a funcionalidade para ler arquivos de texto. Ela lida com a decodificação de bytes para caracteres de forma transparente, também usando o charset padrão do sistema.
Seus construtores mais comuns são:
FileReader(String fileName)
FileReader(File file)A forma mais simples de ler é caractere por caractere, embora seja ineficiente para arquivos grandes:
import java.io.*;
public class Program {
public static void main(String[] args) {
try (FileReader reader = new FileReader("notes.txt")) {
int c;
while ((c = reader.read()) != -1) {
System.out.print((char) c);
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}Para uma performance superior, a leitura deve ser feita em um buffer (um array de caracteres). Esta é a abordagem recomendada:
import java.io.*;
public class Program {
public static void main(String[] args) {
try (FileReader reader = new FileReader("notes.txt")) {
char[] buffer = new char[1024];
int charsRead;
while ((charsRead = reader.read(buffer)) != -1) {
// Constrói uma string apenas com os caracteres que foram efetivamente lidos
System.out.print(new String(buffer, 0, charsRead));
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}Nesta versão, o método read(buffer) tenta preencher o array e retorna o número de caracteres lidos. É crucial usar esse valor de retorno (charsRead) para processar apenas a porção válida do buffer, pois a última leitura quase nunca preencherá o buffer por completo.
Limitações do FileReader e FileWriter
Tanto FileReader quanto FileWriter são convenientes, mas possuem uma limitação perigosa: eles sempre usam a codificação de caracteres padrão do sistema operacional. Isso pode causar problemas de portabilidade. Um arquivo de texto criado em um sistema Windows (que pode ter o padrão windows-1252) pode se tornar ilegível em um sistema Linux com padrão UTF-8.
Para aplicações robustas, é fortemente recomendado especificar a codificação explicitamente. Isso é feito envolvendo streams de bytes com InputStreamReader e OutputStreamWriter:
// Escrita segura com codificação explícita (UTF-8)
try (OutputStreamWriter writer = new OutputStreamWriter(
new FileOutputStream("notes_utf8.txt"), "UTF-8")) {
writer.write("Texto com acentuação.");
}
// Leitura segura com codificação explícita (UTF-8)
try (InputStreamReader reader = new InputStreamReader(
new FileInputStream("notes_utf8.txt"), "UTF-8")) {
// Lógica de leitura...
}Resumo
- Para arquivos de texto, as classes baseadas em
ReadereWritersão a escolha correta. FileWriterescreve caracteres em um arquivo, eFileReaderos lê.- A leitura de arquivos grandes deve ser feita em um buffer (
char[]) para melhor performance. - O maior risco de
FileReadereFileWriteré o uso implícito da codificação padrão do sistema. - Para controle explícito da codificação (prática recomendada), é preferível usar
InputStreamReadereOutputStreamWriter.