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 oInputStream
e decodifica seus bytes em caracteres, usando a codificação padrão do sistema.new BufferedReader(...)
: Envolve oInputStreamReader
para 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
BufferedWriter
eBufferedReader
são classes Decorator que adicionam buffering aWriter
eReader
para otimizar a performance de I/O.BufferedWriter
armazena 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.