Ficheros binarios¶
Excepciones comprobadas¶
Recuerda que tenemos Checked exceptions
que se pueden producir y es obligatorio manejarlas
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());
}
}
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.
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 + '\'' +
'}';
}
}
// 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();
}
}
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;
}
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);
}