Leer archivos en Java

Leer archivos en Java

Te doy la bienvenida a esta segunda parte de la guía de manejo de archivos con Java.

En este capítulo, te voy a enseñar de forma práctica, a leer archivos en Java.

Si te perdiste el capítulo anterior, aquí lo tienes: Introducción al manejo de archivos con Java

Crear un archivo para practicar

Primero de todo, vamos a crear un archivo .txt cualquiera. Escribe algo en él. Si no quieres escribir, aquí tienes el texto que voy a utilizar en los ejemplos:

La pantalla transparente

Un nuevo desarrollo tecnológico promete revolucionar la forma en que interactuamos
con nuestros dispositivos electrónicos. La pantalla transparente, como su nombre lo indica,
es una pantalla que permite ver a través de ella. Esto la hace ideal para aplicaciones
como ventanas inteligentes, pantallas de realidad aumentada y realidad virtual, y pantallas de automóviles.

La tecnología de las pantallas transparentes aún está en sus primeras etapas, pero tiene el
potencial de cambiar la forma en que vivimos y trabajamos. Las pantallas transparentes podrían
hacer que nuestras casas sean más eficientes energéticamente, permitirnos ver el mundo real
mientras usamos dispositivos electrónicos, y mejorar la seguridad en el tráfico.

Prepara el proyecto para leer archivos en Java

Para empezar a leer archivos con Java, vamos a preparar primero un proyecto.

Abre un proyecto de Java. Solo necesitas el archivo Main y un archivo .txt con un texto. Este último, lo voy a colocar en una carpeta llamada archivos. Entonces, te debe quedar así:

Leer archivos con Java

Lo importante, es lo que tienes marcado en la imagen, si tienes alguna cosa diferente en tu proyecto, no es relevante. Esto más que nada, es para que no haya confusión con las rutas de archivo.

Código para leer archivos en Java

Empecemos con la estructura básica del código:

public class Main {
    public static void main(String[] args) {
        try {
          
        } catch () {
            
        }
    }
}

Ahora, pondremos en el bloque try, el código para intentar leer el archivo.

He importado la clase FileInputStream. Esta clase, la utilizamos para el stream de entrada (leer el archivo).

En la línea 6 creo un objeto con la ruta del archivo de texto.

En la línea 7, creo un array de 250 bytes, que servirá para guardar los datos leídos del archivo.

La línea 8 lleva el método read() del objeto (clase FileInputStream). Esta línea lee los datos del archivo en el array de bytes. El método read() devuelve el número de bytes leídos.

Esto es importante porque permite saber cuántos datos han sido leídos del archivo.


Este valor, se va a utilizar en la línea 10, para crear un string final, con el contenido mostrado en la consola en la línea 11. El método constructor new String() toma como parámetros el array de bytes, el índice inicial y el número de bytes a leer.

Por lo tanto, el método new String() crea un objeto String a partir de los datos leídos del archivo, comenzando desde el índice 0 y terminando en el índice bytesRead (250 en este ejemplo).

Finalmente, una vez utilizado el archivo, hay que cerrarlo con el método close() de FileInputStream.

import java.io.FileInputStream;

public class Main {
    public static void main(String[] args) {
        try {
          FileInputStream archivoEntrada = new FileInputStream("archivos/texto.txt");
            byte[] buffer = new byte[250];
            int bytesLeidos = archivoEntrada.read(buffer);

            String texto = new String(buffer, 0, bytesLeidos);
            System.out.println(texto);

            archivoEntrada.close();
        } catch () {
            
        }
    }
}

Manejo de excepciones para leer el archivo

Para leer archivos en Java necesitaremos manejar las posibles excepciones.

Entonces, antes de ejecutarlo, vamos a escribir el manejo de estas.

Importamos la clase IOException y ponemos un mensaje de error, además, mostramos el mensaje técnico de error con getMessage(), para saber exactamente, qué error ha ocurrido.

import java.io.FileInputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try {
            FileInputStream archivoEntrada = new FileInputStream("archivos/texto.txt");
            byte[] buffer = new byte[250];
            int bytesLeidos = archivoEntrada.read(buffer);

            String texto = new String(buffer, 0, bytesLeidos);
            System.out.println(texto);

            archivoEntrada.close();
        } catch (IOException e) {
            System.out.println("Error al leer el archivo: " + e.getMessage());
        }
    }
}

Ejecutar el código

Ya lo tenemos todo listo para ejecutar. Hagamos una prueba.

Resultado en la consola

La pantalla transparente
Un nuevo desarrollo tecnológico promete revolucionar la forma en que interactuamos
con nuestros dispositivos electrónicos. La pantalla transparente, como su nombre lo indica,
es una pantalla que permite ver a través d

El flujo de datos

Vaya, ¿qué ha ocurrido? Mi texto se está cortando.

Esto ocurre, porque el flujo de 250 bytes, es insuficiente para el número de caracteres. Los caracteres, no siempre equivalen a 1 byte. A veces, pueden ocupar más, como por ejemplo, una letra con acento.

En el archivo de texto, selecciona el título "La pantalla transparente". Si tienes un IDE como IntelliJ IDEA, te contará en la parte inferior los caracteres seleccionados. En todo caso, da igual, son 24 caracteres.

Bien, pues vamos a poner un flujo de 24 bytes, para leer solo el título.

byte[] buffer = new byte[24];

Si ejecutas el nuevo código, te sale solo el título completo. El resto de texto, no cabe en el array.

Resultado en la consola

La pantalla transparente

Ahora, supongamos que la palabra pantalla, tuviera acento: "pántalla".

Vamos a colocárlo en el archivo de texto (manualmente).

Ahora, ejecuta el código Java.


Resultado en la consola

La pántalla transparent

Bien, acabamos de comprobar con nuestros propios ojos, que una letra como la a que es un solo caracter, ocupa dos bytes si lleva acento y uno, si no lleva. Entonces, mientras manejemos bytes, hablaremos de bytes, no de caracteres.

Entonces, para almacenar todos los bytes de mi texto, necesitamos un array más grande. Por ejemplo, mi texto tiene 756 caracteres. Puesto que hay acentos y no quiero quedarme corto, pondré 1024, que es una medida, en la cual tengo margen para mi texto.

byte[] buffer = new byte[1024];

Ahora, si lo imprimes en la consola, te saldrá todo el contenido:

Resultado en la consola

La pantalla transparente
Un nuevo desarrollo tecnológico promete revolucionar la forma en que interactuamos
con nuestros dispositivos electrónicos. La pantalla transparente, como su nombre lo indica,
es una pantalla que permite ver a través de ella. Esto la hace ideal para aplicaciones
como ventanas inteligentes, pantallas de realidad aumentada y realidad virtual, y pantallas
de automóviles.
La tecnología de las pantallas transparentes aún está en sus primeras etapas, pero tiene el
potencial de cambiar la forma en que vivimos y trabajamos. Las pantallas transparentes podrían
hacer que nuestras casas sean más eficientes energéticamente, permitirnos ver el mundo real
mientras usamos dispositivos electrónicos, y mejorar la seguridad en el tráfico.

Bucle para leer archivos de texto con Java

El problema que se presenta ahora, es si nos planteamos que este código sirva para cualquier texto. ¿Qué pasa si tengo un TXT de 1.000.000 de bytes?

Podremos pensar que poniendo esto, se solucionaría:

byte[] buffer = new byte[1000000];

Sin embargo, aunque funcione, desperdiciamos mucho espacio en memoria, ya que puede que unas veces el archivo procesado sea de 100 bytes y otras de medio millón.

La solución al dilema, se presenta con un bucle. Simplemente eso.

Ponemos una cantidad relativamente pequeña, como 1024:

byte[] buffer = new byte[1024];

El bucle se ejecutará tantas veces como sea necesario para trabajar con el texto. Por ejemplo, con un texto de 1500 bytes, se ejecutará 2 veces. 1 sola, sería insuficiente.


Explicación del bucle

Te explico. Se leerán 1024 bytes del principio, y le faltarán 476 bytes para leer en la segunda iteración. Entonces, el bucle se ejecutará 2 veces. 1024 bytes + 476 bytes = 1500 bytes.

Fíjate en el bucle y en la línea 9. La variable bytesLeidos, no necesita tener la lectura, ya que se la ponemos a la expresión del bucle.

import java.io.FileInputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try {
            FileInputStream archivoEntrada = new FileInputStream("archivos/texto.txt");
            byte[] buffer = new byte[1024];
            int bytesLeidos;

            while ((bytesLeidos = archivoEntrada.read(buffer)) != -1) {
                String texto = new String(buffer, 0, bytesLeidos);
                System.out.println(texto);
            }

            archivoEntrada.close();
        } catch (IOException e) {
            System.out.println("Error al leer el archivo: " + e.getMessage());
        }
    }
}

Fíjate bien en la declaración del while, en la línea 11.

bytesLeidos es una variable que almacena el número de bytes leídos del archivo de entrada. La función read() del objeto archivoEntrada lee un número determinado de bytes del archivo y los almacena en la variable bytesLeidos. En concreto, hasta 1024, que es su capacidad.

El operador != compara dos valores y devuelve true si los valores son diferentes y false si son iguales. En este caso, estamos comparando el valor de la variable bytesLeidos con el valor -1.

El valor -1 es un valor especial que indica que se ha llegado al final del archivo. Esto se debe a que el método read() de la clase FileInputStream devuelve -1 cuando no hay más bytes que leer. Por lo tanto, el bucle while continuará ejecutándose mientras el valor de la variable bytesLeidos sea diferente de -1.

Que es lo mismo que decir, se lee hasta que no haya más que leer.

Entonces, con mi texto de ejemplo, le basta con una iteración, ya que no llega a los 800 bytes. Sin embargo, con otras, necesitará más de una.

Prueba con un marcador

Vamos a hacer la prueba:

while ((bytesLeidos = archivoEntrada.read(buffer)) != -1) {
  String texto = new String(buffer, 0, bytesLeidos);
  System.out.println(texto);
  System.out.println("FIN DE LA ITERACIÓN.");
}

Antes de finalizar cada iteración, se va a imprimir ese mensaje "FIN DE LA ITERACIÓN". Así en la salida, veremos cuantas veces se itera.

Para hacer una prueba de más de una iteración, cambia el valor del array de 1024 a 200.

byte[] buffer = new byte[200];

Esta vez, al ejecutarlo, veremos que necesita realizar 4 iteraciones. Esto le da una capacidad de 800 bytes en total (200 * 4 = 800).

Mi texto, necesita 3 iteraciones con el array completo (3 * 200 = 600) y me quedan 100 y pico. Esto necesita una iteración más de hasta 200 bytes, aunque no los necesite todos.


Resultado en la consola

La pantalla transparente
Un nuevo desarrollo tecnológico promete revolucionar la forma en que interactuamos
con nuestros dispositivos electrónicos. La pantalla transparente, como su nombre lo in
FIN DE LA ITERACIÓN.
dica,
es una pantalla que permite ver a través de ella. Esto la hace ideal para aplicaciones
como ventanas inteligentes, pantallas de realidad aumentada y realidad virtual, y pantallas
de automóv
FIN DE LA ITERACIÓN.
iles.
La tecnología de las pantallas transparentes aún está en sus primeras etapas, pero tiene el
potencial de cambiar la forma en que vivimos y trabajamos. Las pantallas transparentes podrían
FIN DE LA ITERACIÓN.
hacer que nuestras casas sean más eficientes energéticamente, permitirnos ver el mundo real
mientras usamos dispositivos electrónicos, y mejorar la seguridad en el tráfico.
FIN DE LA ITERACIÓN.

Según el programa que hagas, valora que tamaño de array pones. 1024 iría bien para archivos medianos, pero para archivos muy pequeños, sería excesivo, aunque tampoco alarmante.

Para archivos muy grandes, puede que 1024 se quede algo corto y necesitemos miles de iteraciones.

Problemas con los strings

Seguramente, hayas visto que en cada iteración, se corta el texto. Este resultado no es válido para nada que se tenga que presentar a un usuario final.

Para solucionar esto, puedes utilizar esta modificación utilizando la clase StringBuilder. Gracias a esto, en lugar de crear cuatro strings (si hay cuatro iteraciones), crearemos uno y luego, añadimos cada porción al final de la anterior, con cada iteración. Es decir, creamos un solo string, que va uniendo todas las partes iteradas.

public class Main {
    public static void main(String[] args) {
        try {
            FileInputStream archivoEntrada = new FileInputStream("archivos/texto.txt");
            byte[] buffer = new byte[200];
            int bytesLeidos;
            StringBuilder texto = new StringBuilder();

            while ((bytesLeidos = archivoEntrada.read(buffer)) != -1) {
                texto.append(new String(buffer, 0, bytesLeidos));
            }

            System.out.println(texto);

            archivoEntrada.close();
        } catch (IOException e) {
            System.out.println("Error al leer el archivo: " + e.getMessage());
        }
    }
}

Si en algún momento, necesitas algo de la clase String con el texto generado, puedes transformarlo con el método tostring():

texto.toString()

Seguimos con la escritura de archivos con Java, en el siguiente capítulo.


Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

servidores de archivos Entrada anterior Introducción al manejo de archivos con Java
curso Java Entrada siguiente Escribir archivos con Java