Processamento de Texto Eficiente com Java BufferedReader

JavaBeginner
Pratique Agora

Introdução

Neste laboratório, utilizaremos a classe Java BufferedReader, que faz parte do pacote java.io. O BufferedReader é usado para ler texto de um fluxo de entrada de caracteres e armazena os caracteres em um buffer para melhor desempenho. A classe envolve outras classes de leitura (reader) e fluxos de entrada (input streams), permitindo-nos melhorar a eficiência geral e o desempenho de nossos programas. Exploraremos os diferentes construtores, métodos e casos de uso do BufferedReader para entender sua funcionalidade.

Criar um BufferedReader com um FileReader

O construtor BufferedReader recebe um objeto Reader como parâmetro. Podemos criar um reader usando um FileReader, que lê arquivos de texto na codificação padrão. Nesta etapa, leremos um arquivo com a classe BufferedReader envolvendo um FileReader.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderDemo {
    public static void main(String[] args) {
        String path = "path/to/your/file.txt";
        try {
            FileReader fileReader = new FileReader(path);
            BufferedReader bufferedReader = new BufferedReader(fileReader); //Creating a BufferedReader by wrapping the FileReader
            // Adicione o código para ler dados do arquivo aqui

            bufferedReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Para executar o código, compile o arquivo com javac BufferedReaderDemo.java e, em seguida, execute-o com java BufferedReaderDemo.

Usar um BufferedReader com um Stream

Também podemos criar um BufferedReader usando um fluxo de entrada (input stream) como fonte. Nesta etapa, envolveremos um InputStreamReader com BufferedReader e leremos dados de System.in. Isso lerá os dados que inserimos usando o teclado.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class BufferedReaderDemo {
    public static void main(String[] args) {
        try {
            InputStreamReader isr = new InputStreamReader(System.in);
            BufferedReader bufferedReader = new BufferedReader(isr); //Creating a BufferedReader
            System.out.println("Enter Something:");
            String line = bufferedReader.readLine(); //Read input from console
            System.out.println("You Entered: " + line); //Print out the input

            bufferedReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Fechando o Stream

Devemos sempre usar o método close() do BufferedReader para liberar todos os recursos do sistema associados ao reader. Para simplificar o código, podemos usar um bloco try-with-resources para fechar automaticamente o stream.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderDemo {
    public static void main(String[] args) {
        String path = "path/to/your/file.txt";
        try (FileReader fileReader = new FileReader(path);
             BufferedReader bufferedReader = new BufferedReader(fileReader)) {

            // Add code to read data from the file here

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

BufferedReader vs. Scanner

Tanto Scanner quanto BufferedReader podem ser usados para ler dados de fontes externas. No entanto, existem algumas diferenças entre essas duas classes. Nesta etapa, compararemos algumas diferenças chave.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Scanner;

public class BufferedReaderDemo {
    public static void main(String[] args) {
        String path = "path/to/your/file.txt";
        try (FileReader fileReader = new FileReader(path);
             BufferedReader bufferedReader = new BufferedReader(fileReader);
             Scanner scanner = new Scanner(fileReader)) {
            long startTime = System.nanoTime();
            // Reading file with BufferedReader
            bufferedReader.readLine();
            while (bufferedReader.readLine() != null) ;
            long endTime = System.nanoTime();
            System.out.println("Time taken by BufferedReader : " + (endTime - startTime) + "ns");

            startTime = System.nanoTime();
            // Reading file with Scanner
            scanner.nextLine();
            while (scanner.hasNextLine()) scanner.nextLine();
            endTime = System.nanoTime();
            System.out.println("Time taken by Scanner : " + (endTime - startTime) + "ns");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Este código compara o tempo gasto por BufferedReader e Scanner para ler dados do mesmo arquivo. Os tempos variarão, mas a saída informará qual deles demorou mais.

Lendo Arquivos Linha por Linha

A classe BufferedReader fornece vários métodos para ler dados. O método readLine() lê uma linha por vez. Este método retorna null se atingirmos o final do stream. Nesta etapa, leremos um arquivo usando BufferedReader.readLine().

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderDemo {
    public static void main(String[] args) {
        String path = "path/to/your/file.txt";
        try (FileReader fileReader = new FileReader(path);
             BufferedReader bufferedReader = new BufferedReader(fileReader)) {

            String line;
            while ((line = bufferedReader.readLine()) != null) {
                System.out.println(line);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Lendo Caracteres Individuais de um Arquivo

Também podemos ler um único caractere por vez usando o método read() da classe BufferedReader. Este método retorna o caractere lido como um inteiro. Se atingirmos o final do stream, ele retorna -1.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderDemo {
    public static void main(String[] args) {
        String path = "path/to/your/file.txt";
        try (FileReader fileReader = new FileReader(path);
             BufferedReader bufferedReader = new BufferedReader(fileReader)) {

            int charRead;
            while ((charRead = bufferedReader.read()) != -1) {
                System.out.print((char) charRead);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Lendo Múltiplos Caracteres de um Arquivo

O método read() pode ler múltiplos caracteres de uma vez. Precisamos passar um array de char no qual ele armazenará os dados. Também precisamos usar um deslocamento (offset) que indica o índice inicial do array de char. Os dados armazenados começam a partir deste índice. Também precisamos mencionar o comprimento máximo de caracteres a serem lidos.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderDemo {
    public static void main(String[] args) {
        String path = "path/to/your/file.txt";
        try (FileReader fileReader = new FileReader(path);
             BufferedReader bufferedReader = new BufferedReader(fileReader)) {

            char[] charArr = new char[5];
            bufferedReader.read(charArr, 0, 5);
            System.out.print(charArr);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Ignorando Caracteres

A classe BufferedReader fornece um método skip(), que podemos usar para ignorar caracteres. Ele recebe um parâmetro do tipo long.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderDemo {
    public static void main(String[] args) {
        String path = "path/to/your/file.txt";
        try (FileReader fileReader = new FileReader(path);
             BufferedReader bufferedReader = new BufferedReader(fileReader)) {

            int charRead;
            StringBuilder sb = new StringBuilder();
            while ((charRead = bufferedReader.read()) != -1) {
                if (charRead != '*') {
                    sb.append((char) charRead);
                } else {
                    bufferedReader.skip(1);
                }
            }
            System.out.println(sb);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Marcação e Reset

A classe BufferedReader nos fornece o método mark() para marcar um caractere específico. Podemos voltar a este caractere marcado a qualquer momento no futuro usando o método reset(). O método mark() recebe um inteiro como entrada que denota o número máximo de bytes que podem ser lidos antes que a marca se torne inválida.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderDemo {
    public static void main(String[] args) {
        String path = "path/to/your/file.txt";
        try (FileReader fileReader = new FileReader(path);
             BufferedReader bufferedReader = new BufferedReader(fileReader)) {

            char[] charArr = new char[5];
            charArr[0] = (char) bufferedReader.read();
            charArr[1] = (char) bufferedReader.read();

            bufferedReader.mark(10);
            bufferedReader.skip(1);
            charArr[2] = (char) bufferedReader.read();
            charArr[3] = (char) bufferedReader.read();
            charArr[4] = (char) bufferedReader.read();
            System.out.println(charArr);

            bufferedReader.reset();
            char asterisk = (char) bufferedReader.read();
            System.out.print(asterisk);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Resumo

Neste laboratório, usamos a classe Java BufferedReader para ler texto de um fluxo de entrada de caracteres. Aprendemos como encapsular outras classes de leitura (reader classes) e fluxos de entrada (input streams) com BufferedReader para melhorar o desempenho e a eficiência. Cobrimos tópicos como leitura de caracteres únicos e múltiplos de um arquivo, ignorando caracteres, marcando e resetando posições em um arquivo, e comparando as classes BufferedReader e Scanner. Também usamos blocos try-with-resources para fechar automaticamente o fluxo e evitar vazamentos de memória.