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
Reader
eWriter
são a escolha correta. FileWriter
escreve caracteres em um arquivo, eFileReader
os lê.- A leitura de arquivos grandes deve ser feita em um buffer (
char[]
) para melhor performance. - O maior risco de
FileReader
eFileWriter
é o uso implícito da codificação padrão do sistema. - Para controle explícito da codificação (prática recomendada), é preferível usar
InputStreamReader
eOutputStreamWriter
.