Saltar a contenido

Ficheros binarios

alt text

Los fichero binarios se encuentran guardados con estructuras que dependen del programa con el que se van a utilizar. Son ejemplo:

JavaIO

Excepciones comprobadas

Recuerda que tenemos Checked exceptions que se pueden producir y es obligatorio manejarlas

Excepciones

Las clase de I/O te obligan a trabajar con try/catch

Escribir datos en un fichero binario

La escritura en ficheros binarios representa el flujo de salida de bytes, es decir, necesitamos utilizar las clases que derivan de OutputStream. Se puede escribir datos en un fichero binario de muchas formas. Veamos un ejemplo utilizando la clase FileOutputStream.

 public static void main(String[] args) {
        DataOutputStream fos=null;
        try {
            fos= new DataOutputStream(new FileOutputStream("datos.dat"));
            fos.writeInt(0);
            fos.writeInt(-34);
            //todas pueden derivar de IOException
        } catch (FileNotFoundException e) {
            System.out.println(e.getMessage());
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }finally{//siempre se ejecuta y permite cerrar
            if(fos!=null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

En el ejemplo, el constructor FileOutputStream abre el fichero datos.dat para escritura. Se crea un nuevo fichero; si un fichero antiguo tiene el mismo nombre, se eliminará. Luego, un DataOutputStream se conecta al FileOutputStream.

DataOutputStream tiene métodos para escribir datos primitivos en un flujo de salida. El método writeInt() escribe los cuatro bytes de un tipo de datos int en la secuencia.

El programa escribe cuatro enteros en el flujo de salida y luego cierra el flujo.

Warning

Siempre hay que cerrar el flujo para asegurarse de liberar todos los recursos asociados a él y que el sistema operativo no consuma recursos.

La excepción IOException se lanza si el stream se ha cerrado y el flujo de salida contenido no admite la escritura después del cierre, o se produce otro error de I/O.

try-with-resources

La declaración try-with-resource es una característica introducida en Java 7 que facilita la gestión de recursos que deben cerrarse explícitamente, como flujos de archivos o conexiones a bases de datos. Permite que los recursos sean automáticamente cerrados una vez que el bloque try termina, incluso si ocurre una excepción. Esto simplifica el código y reduce la posibilidad de fugas de recursos.

public static void main(String[] args) {
    try(DataOutputStream fos = new DataOutputStream(new FileOutputStream("datos.dat"))) {

        fos.writeInt(0);
        fos.writeInt(-34);

    } catch (FileNotFoundException e) { //todas pueden derivar de IOException
        System.out.println(e.getMessage());
    } catch (IOException e) {
        System.out.println(e.getMessage());
    }
}
Pero podemos seguir usando la versión con finally

Escritura en un fichero binario usando el búfer

Ahora vamos a escribir más enteros en el fichero. Para ello utilizamos BufferedOutputStream. Este búfer almacena los bytes antes de que se escriban en el disco. Recuerda que un búfer es un bloque de memoria que se usa para ensamblar datos antes de que se escriban todos a la vez.

JavaIO

Cuando se cierra el stream close(), es como si pincháramos en guardar de forma gráfica, es decir, que si no cerramos el stream es como si el fichero estuviera vacío.

El almacenamiento en búfer hace que las operaciones de E/S sean más eficientes. Para un programa que realiza E/S masivas, el almacenamiento en búfer es esencial. La E/S es muy lenta en comparación con las operaciones con almacenamiento principal. Sin almacenamiento en búfer, la E/S sería muy, muy lenta.

 /**
 * Método writeBuffer()
 *
 * Escribe 1000 números enteros en el fichero "datosBufer.dat"
 * utilizando:
 *
 * - FileOutputStream        → conexión con el fichero
 * - BufferedOutputStream    → mejora el rendimiento (usa memoria intermedia)
 * - DataOutputStream        → permite escribir datos primitivos (int, double, etc.)
 *
 * Se utiliza try-with-resources para cerrar automáticamente el flujo.
 */
public static void writeBuffer() {

    // try-with-resources:
    // El recurso declarado dentro del paréntesis se cierra automáticamente
    try (DataOutputStream out =
                 new DataOutputStream(             // Permite escribir tipos primitivos
                         new BufferedOutputStream( // Añade buffer (mejora rendimiento)
                                 new FileOutputStream("datosBufer.dat")))) { // Conexión al fichero

        // Escribimos 1000 enteros en el fichero
        for (int i = 0; i < 1000; i++) {

            // writeInt escribe 4 bytes por cada entero
            out.writeInt(i);
        }

        // No hace falta cerrar el stream manualmente
        // Se cerrará automáticamente al salir del try

    } catch (IOException e) {

        // Capturamos cualquier error de escritura o creación del fichero
        throw new RuntimeException(e);
    }
}

Lectura de datos en un fichero binario

Para leer un fichero, primero hemos de preguntarnos un poco sobre él, es decir, si fue escrito por un programa Java, entonces necesitaremos saber qué tipos de datos se usaron, y así poder usar una subclase de InputStream para leer bytes para ese tipo de datos.

InputStream, como hemos visto en los diagramas, es una clase abstracta para objetos que leen flujos de bytes. Aunque, no todas las clases que derivan de ella están relacionadas con la entrada de ficheros de disco. Por ejemplo, PipedInputStream representa datos provenientes de otro programa en ejecución.

public static void main(String[] args) {
    try( DataInputStream dis = new DataInputStream(new FileInputStream("datos.dat"));) {

        //leer todos los enteros de un fichero
        while (dis.available() > 0) {//me va diciendo cuántos bytes disponibles hay en el input stream
            //mientras hayan bytes que leer
            System.out.println(dis.readInt());
        }

    } catch (FileNotFoundException e) { //todas pueden derivar de IOException
        System.out.println(e.getMessage());
    } catch (IOException e) {
        System.out.println(e.getMessage());

    }
}

Al igual que en la escritura para la lectura también disponemos de la clase BufferedInputStream.

Lectura y escritura de Objetos en ficheros

Para poder guardar objetos de una Clase en un fichero es necesario que la clase implemente la interface Serializable. Esta interface no te obliga a implementar ningún método. Vamos a ver un ejemplo donde guardamos una ArrayList y luego lo recuperamos

alt text

public class Socio implements Serializable {
    //nombre del fichero
    public static final String SOCIOS_DAT = "socios.dat";
    // private static final long serialVersionUID = 1L;
    private String dni;
    private String nombre;

    public Socio(String dni, String nombre) {
        this.dni = dni;
        this.nombre = nombre;
    }
    @Override
    public String toString() {
        return "Socio{" +
                "dni='" + dni + '\'' +
                ", nombre='" + nombre + '\'' +
                '}';
    }
}
Para la escritura del ArrayList de Socios en el fichero podemos llamar al siguiente método
// Método para guardar en un archivo binario
    /**
 * Guarda una lista de objetos Socio en un archivo binario.
 *
 * Este método:
 * 1. Crea un ObjectOutputStream conectado al fichero indicado.
 * 2. Serializa (convierte en binario) el ArrayList completo.
 * 3. Cierra automáticamente el flujo gracias a try-with-resources.
 *
 * Requisito importante:
 * La clase Socio debe implementar Serializable.
 *
 * @param socios        Lista de socios que se desea guardar
 * @param nombreArchivo Nombre del fichero binario (ej: "socios.dat")
 */
public static void guardarEnArchivo(ArrayList<Socio> socios, String nombreArchivo) {

    // try-with-resources:
    // El ObjectOutputStream se cerrará automáticamente al finalizar el bloque
    try (ObjectOutputStream oos =
                 new ObjectOutputStream(
                         new FileOutputStream(nombreArchivo))) {

        // writeObject serializa el objeto completo (la lista y todos sus elementos)
        oos.writeObject(socios);

    } catch (IOException e) {

        // Se captura cualquier error de escritura o creación del fichero
        e.printStackTrace();
    }
}
Para la lectura
/**
 * Lee una lista de objetos Socio desde un archivo binario.
 *
 * Este método:
 * 1. Abre un ObjectInputStream conectado al fichero indicado.
 * 2. Lee el objeto serializado con readObject().
 * 3. Comprueba que el objeto leído sea un ArrayList.
 * 4. Realiza un casting seguro a ArrayList<Socio>.
 *
 * @param nombreArchivo Nombre del fichero binario (ej: "socios.dat")
 * @return ArrayList<Socio> leída del fichero o null si ocurre error
 */
public static ArrayList<Socio> leerDesdeArchivo(String nombreArchivo) {

    ArrayList<Socio> socios = null;

    // try-with-resources:
    // El ObjectInputStream se cerrará automáticamente
    try (ObjectInputStream ois =
                 new ObjectInputStream(
                         new FileInputStream(nombreArchivo))) {

        // readObject() devuelve un Object genérico
        Object obj = ois.readObject();

        // Comprobamos que el objeto leído sea realmente un ArrayList
        if (obj instanceof ArrayList) {

            // Hacemos casting explícito a ArrayList<Socio>
            socios = (ArrayList<Socio>) obj;
        }

    } catch (IOException | ClassNotFoundException e) {

        // IOException → error de lectura del fichero
        // ClassNotFoundException → no encuentra la clase Socio
        e.printStackTrace();
    }

    // Devolvemos la lista leída (o null si hubo error)
    return socios;
}
Un ejemplo de guardado y recuperación
 public static void main(String[] args) {
        // Crear algunos socios y guardarlos en un ArrayList
        ArrayList<Socio> socios = new ArrayList<>();
        socios.add(new Socio("12345678A", "Juan Perez"));
        socios.add(new Socio("87654321B", "María Lopez"));
        socios.add(new Socio("98765432C", "Pedro Gomez"));

        // Guardar los socios en un archivo binario
        guardarEnArchivo(socios, SOCIOS_DAT);

        // Leer los socios desde el archivo binario
        ArrayList<Socio> sociosLeidos = leerDesdeArchivo(SOCIOS_DAT);

        // Imprimir los socios leídos
        System.out.println("Socios leídos:");
        if(sociosLeidos!=null)
            sociosLeidos.forEach(System.out::println);
}