Saltar a contenido

Ficheros binarios

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.

 public static void writeBuffer() {
    DataOutputStream out =null;
    try {
        out = new DataOutputStream(
                new BufferedOutputStream(
                        new FileOutputStream("datosBufer.dat")));
        for (int i = 0; i < 1000; i++) {
            out.writeInt(i);
        }

    } catch (FileNotFoundException e) {
        throw new RuntimeException(e);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }finally{//siempre se ejecuta y permite cerrar
       if(fos!=null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    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 que no es obligatorio implementar ningún método. Vamos a ver un ejemplo donde guardamos una ArrayList y luego lo recuperamos

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
    public static void guardarEnArchivo(ArrayList<Socio> socios, String nombreArchivo) {
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(nombreArchivo))) {
            oos.writeObject(socios);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
Para la lectura
public static ArrayList<Socio> leerDesdeArchivo(String nombreArchivo) {
        ArrayList<Socio> socios = null;
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(nombreArchivo))) {
            Object obj = ois.readObject();
            if (obj instanceof ArrayList) {
                socios = (ArrayList<Socio>) obj;
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        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);
}