Procesamiento de texto eficiente con Java BufferedReader

JavaBeginner
Practicar Ahora

Introducción

En este laboratorio, usaremos la clase BufferedReader de Java, que es parte del paquete java.io. BufferedReader se utiliza para leer texto desde un flujo de entrada de caracteres y bufferiza los caracteres para una mejor rendimiento. La clase envuelve otras clases de lectura y flujos de entrada, lo que nos permite mejorar la eficiencia y el rendimiento general de nuestros programas. Exploraremos los diferentes constructores, métodos y casos de uso de BufferedReader para comprender su funcionalidad.

Crear un BufferedReader con un FileReader

El constructor de BufferedReader toma un objeto Reader como parámetro. Podemos crear un lector usando un FileReader, que lee archivos de texto en la codificación predeterminada. En este paso, leeremos un archivo con la clase BufferedReader al envolver un 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); //Creando un BufferedReader al envolver el FileReader
            // Agregue código para leer datos del archivo aquí

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

Para ejecutar el código, compile el archivo con javac BufferedReaderDemo.java, y luego ejecútelo con java BufferedReaderDemo.

Usar un BufferedReader con un flujo

También podemos crear un BufferedReader usando un flujo de entrada como fuente. En este paso, envolveremos un InputStreamReader con BufferedReader y leeremos datos desde System.in. Esto leerá los datos que ingresamos usando el 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); //Creando un BufferedReader
            System.out.println("Ingrese algo:");
            String line = bufferedReader.readLine(); //Leer la entrada desde la consola
            System.out.println("Usted ingresó: " + line); //Imprimir la entrada

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

Cerrando el flujo

Siempre debemos usar el método close() del BufferedReader para liberar todos los recursos del sistema asociados con el lector. Para simplificar el código, podemos usar un bloque try-with-resources para cerrar automáticamente el flujo.

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)) {

            // Agregue código para leer datos del archivo aquí

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

BufferedReader Vs. Scanner

Tanto Scanner como BufferedReader se pueden usar para leer datos desde fuentes externas. Sin embargo, hay algunas diferencias entre estas dos clases. En este paso, compararemos algunas diferencias clave.

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();
            // Leyendo el archivo con BufferedReader
            bufferedReader.readLine();
            while (bufferedReader.readLine()!= null) ;
            long endTime = System.nanoTime();
            System.out.println("Tiempo tomado por BufferedReader : " + (endTime - startTime) + "ns");

            startTime = System.nanoTime();
            // Leyendo el archivo con Scanner
            scanner.nextLine();
            while (scanner.hasNextLine()) scanner.nextLine();
            endTime = System.nanoTime();
            System.out.println("Tiempo tomado por Scanner : " + (endTime - startTime) + "ns");

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

Este código compara el tiempo que tardan BufferedReader y Scanner en leer datos del mismo archivo. Los tiempos variarán, pero la salida te dirá cuál de los dos tardó más.

Leyendo archivos línea por línea

La clase BufferedReader proporciona varios métodos para leer datos. El método readLine() lee una línea a la vez. Este método devuelve null si llegamos al final del flujo. En este paso, leeremos un archivo 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();
        }
    }
}

Leyendo caracteres individuales de un archivo

También podemos leer un solo carácter a la vez usando el método read() de la clase BufferedReader. Este método devuelve el carácter leído como un entero. Si llegamos al final del flujo, entonces devuelve -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();
        }
    }
}

Leyendo múltiples caracteres de un archivo

El método read() puede leer múltiples caracteres a la vez. Necesitamos pasar un arreglo de chars en el que almacenará los datos. También necesitamos usar un desplazamiento que indique el índice de inicio del arreglo de chars. Los datos almacenados empiezan a partir de este índice. También necesitamos mencionar la longitud máxima de caracteres a leer.

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();
        }
    }
}

Saltando caracteres

La clase BufferedReader proporciona un método skip(), que podemos usar para saltar caracteres. Toma un parámetro de 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();
        }
    }
}

Marcar y restablecer

La clase BufferedReader nos proporciona el método mark() para marcar un carácter específico. En cualquier momento en el futuro, podemos volver a este carácter marcado mediante el uso del método reset(). El método mark() toma un entero como entrada que denota los bytes máximos que se pueden leer antes de que el marcado deje de ser válido.

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();
        }
    }
}

Resumen

En este laboratorio, usamos la clase Java BufferedReader para leer texto desde un flujo de entrada de caracteres. Aprendimos cómo envolver otras clases de lectores y flujos de entrada con BufferedReader para mejorar el rendimiento y la eficiencia. Cubrimos temas como la lectura de un solo carácter y múltiples caracteres de un archivo, el salto de caracteres, el marcado y el restablecimiento de posiciones en un archivo, y la comparación de las clases BufferedReader y Scanner. También usamos bloques try-with-resources para cerrar automáticamente el flujo y evitar fugas de memoria.