Buffering de Streams de Caracteres em Java com BufferedReader e BufferedWriter
Assim como os streams de bytes se beneficiam de buffers, os streams de caracteres (Reader e Writer) também possuem suas classes "decoradoras" para otimizar o desempenho: BufferedReader e BufferedWriter. Envolver FileReader e FileWriter com essas classes é a prática padrão para manipular arquivos de texto em Java, pois minimiza o número de acessos ao disco, que são operações lentas.
Escrita de Texto com Buffer usando BufferedWriter
A classe BufferedWriter envolve um Writer existente e armazena os caracteres a serem gravados em um buffer interno. A escrita no dispositivo físico só ocorre quando o buffer está cheio, quando o stream é fechado (close()), ou quando o método flush() é chamado. Isso reduz drasticamente a sobrecarga de I/O.
BufferedWriter possui os seguintes construtores:
BufferedWriter(Writer out)
BufferedWriter(Writer out, int sz)O primeiro parâmetro é o Writer de destino. O segundo, opcional, permite especificar o tamanho do buffer.
O exemplo a seguir demonstra a escrita em um arquivo:
import java.io.*;
public class Program {
public static void main(String[] args) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter("notes.txt"))) {
String text = "Hello World!\nHey! Teachers! Leave the kids alone.";
bw.write(text);
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}Neste código, a chamada a bw.write(text) é extremamente rápida, pois apenas copia os caracteres para o buffer em memória. A estrutura try-with-resources garante que, ao final do bloco, bw.close() seja chamado. O método close(), por sua vez, chama flush() para assegurar que todos os dados do buffer sejam efetivamente gravados no arquivo.
Leitura de Texto com Buffer usando BufferedReader
Enquanto BufferedReader também melhora a performance da leitura, seu principal atrativo é um método que não existe na classe Reader base: readLine().
BufferedReader possui os seguintes construtores:
BufferedReader(Reader in)
BufferedReader(Reader in, int sz)O método readLine() lê uma linha inteira de texto e a retorna como uma String, removendo os caracteres de terminação de linha (\n, \r, ou \r\n). Quando o final do stream é atingido, o método retorna null. Esta é a forma mais comum e idiomática de processar arquivos de texto em Java.
import java.io.*;
public class Program {
public static void main(String[] args) {
try (BufferedReader br = new BufferedReader(new FileReader("notes.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}Exemplo Prático: Lendo do Console e Escrevendo em um Arquivo
As classes decoradoras podem ser encadeadas para construir funcionalidades complexas de forma elegante. O exemplo a seguir lê a entrada do usuário no console e a salva em um arquivo, linha por linha, até que o usuário digite "ESC".
import java.io.*;
public class Program {
public static void main(String[] args) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new FileWriter("user_input.txt"))) {
String text;
while (!(text = br.readLine()).equalsIgnoreCase("ESC")) {
bw.write(text);
bw.newLine(); // Método conveniente para adicionar quebra de linha do sistema
bw.flush(); // Garante que cada linha seja escrita imediatamente
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}Vamos analisar a cadeia de leitura do console:
System.in: É umInputStream, ou seja, um fluxo de bytes brutos vindo do teclado.new InputStreamReader(System.in): É a "ponte" que envolve oInputStreame decodifica seus bytes em caracteres, usando a codificação padrão do sistema.new BufferedReader(...): Envolve oInputStreamReaderpara adicionar o buffering e, mais importante, o métodoreadLine().
Dentro do laço, bw.flush() é chamado após cada escrita. Em um programa interativo, isso é crucial para garantir que o texto seja gravado no arquivo assim que o usuário pressiona Enter, em vez de esperar o buffer encher ou o programa terminar.
Resumo
BufferedWritereBufferedReadersão classes Decorator que adicionam buffering aWritereReaderpara otimizar a performance de I/O.BufferedWriterarmazena caracteres em um buffer e os escreve em grandes blocos. Ele também oferece o métodonewLine()para portabilidade.- A principal vantagem do
BufferedReaderé o métodoreadLine(), que simplifica a leitura de arquivos de texto linha por linha. - A leitura de texto do console (
System.in) é feita com a cadeiaBufferedReader(new InputStreamReader(System.in)). - Em aplicações interativas, o uso de
flush()pode ser necessário para garantir que os dados sejam gravados em tempo real.