Continuamos con la entrada y salida de ficheros en el curso de Java de RedesZone.net. Para los que no os acordéis, en la entrega anterior, hablamos sobre todo lo referido a la clase Scanner de Java, que nos facilitaba la lectura de ficheros de texto que siguen un determinado patrón.
En la entrega de hoy, que será la última entrega relacionada con entrada y salida, hablaremos sobre cómo utilizar los ficheros como tablas, es decir, si tenemos la necesidad, cómo es posible acceder a cualquier posición del fichero como si de una tabla se tratase.
Hasta ahora todos los ficheros se han manejado mediante clases que representan secuencias
En muchas ocasiones es conveniente poder acceder a los datos en cualquier orden, como en una tabla
- Las diferencias con un array son:
- Tamaño no limitado a priori
- Memoria persistente (no volátil)
Para ello Java dispone de la clase RandomAccessFile
- Permite acceso aleatorio al fichero
- No es un Stream
- Se puede usar para leer, o leer y escribir a la vez
En java son ficheros que contienen bytes
Se numeran con un índice que empieza en cero. Tienes varias opciones de cursos de Java por Internet.
Existe un índice almacenado en el sistema que se llama el puntero de lectura/escritura
- Las lecturas y escrituras se hacen a partir de él, en secuencia
- Es posible cambiar el puntero de lectura/escritura
Si escribimos pasado el final del fichero, su tamaño se amplía
Operaciones más habituales
Ejemplo con tabla de datos
[java]
import java.io.*;
/**
* Clase que contiene los datos de un libro
*/
public class Libro {
// constantes estáticas
public static final int maxCaracteresTítulo=100;
public static final int tamañoEnBytes =
maxCaracteresTitulo+1 // título + n
+Integer.SIZE/8 // publicado
+Double.SIZE/8 // precio
+1; // tamaño par
// atributos privados
private String título;
private int publicado; // año de publicación
private double precio; // en euros
/**
* Constructor al que se le pasan los datos del
* libro
*/
public Libro(String titulo, int publicado,
double precio) {
// asegurarse de que el titulo no supera
// maxCaracteresTítulo
if (titulo.length()>maxCaracteresTítulo) {
this.titulo=
titulo.substring(0,maxCaracteresTítulo);
} else {
this.titulo=titulo;
}
this.publicado=publicado;
this.precio=precio;
}
/**
* Lee de fichero
*/
public static Libro leeDeFichero(
RandomAccessFile fich) throws IOException {
// lee los tres datos, por orden
int publi=fich.readInt();
double prec=fich.readDouble();
String tit=fich.readLine().trim();
// crea y retorna el libro
return new Libro(tit,publi,prec);
}
/**
* Escribe en el fichero
*/
public void escribeEnFichero(
RandomAccessFile fich) throws IOException {
// escribe los tres datos, por orden
fich.writeInt(publicado);
fich.writeDouble(precio);
fich.writeBytes(titulo+’n’);
}
métodos observadores, toString, …
} // clase Libro
[/java]
[java]
import java.io.*;
/**
* Tabla de libros persistente almacenada en
* un fichero de acceso aleatorio
*/
public class TablaLibros {
// atributos privados
private RandomAccessFile fich;
/**
* Constructor al que se le pasa el nombre
* del fichero
*/
public TablaLibros(String nombreFichero)
throws FileNotFoundException {
fich = new RandomAccessFile(nombreFichero,»rw»);
}
/**
* Obtener el elemento de la tabla que esta en
* «índice»
*/
public Libro obten(int índice)
throws IOException {
// posiciona el contador de lectura/escritura
long pos=indice*Libro.tamañoEnBytes;
fich.seek(pos);
// lee y retorna el libro
return Libro.leeDeFichero(fich);
}
/** Escribir un libro en la posición «índice»
* de la tabla */
public void almacena(int índice, Libro l)
throws IOException {
// posiciona el contador de lectura/escritura
long pos=indice*Libro.tamañoEnBytes;
fich.seek(pos);
// escribe el libro
l.escribeEnFichero(fich);
}
/** Cerrar la tabla */
public void cerrar() throws IOException {
fich.close();
}
} // clase TablaLibros
[/java]
[java]
// ejemplo de uso de TablaLibros
TablaLibros t = null;
try {
t = new TablaLibros(«random.dat»);
Libro libro1 = new Libro(«Java», 2006, 15.0);
Libro libro2 = new Libro(«1984», 1949, 25.0);
t.almacena(0,libro1);
t.almacena(1,libro2);
Libro l1= t.obten(0);
Libro l2= t.obten(1);
} finally {
if (t != null) {
t.cerrar();
}
}
[/java]
Para escribir objetos en ficheros de acceso aleatorio es preciso convertirlos a un array de bytes
- A este proceso se le llama “serialización”
Para leer el objeto hay que “deserializarlo”
La clase ByteArrayOutputStream nos ayuda en este proceso
Tener cuidado pues la serialización incluye objetos a los que se hace referencia
- Esto puede hacer que el tamaño del objeto serializado varíe mucho
- Para acceso aleatorio es conveniente tener tamaños fijos
Ejemplo de serialización
[java]
// serializa un libro y le escribe en un fichero
RandomAccessFile fich = null;
try {
// abre el fichero de acceso aleatorio
fich = new RandomAccessFile (nomFich, «rw»);
// pone el puntero al principio
fich.seek(0L);
// serializa el libro convirtiéndolo a una
// secuencia de bytes
ByteArrayOutputStream bos =
new ByteArrayOutputStream();
ObjectOutputStream out =
new ObjectOutputStream(bos);
out.writeObject(libro);
out.close();
// obtiene los bytes del libro serializado
byte[] buf = bos.toByteArray();
// escribe los bytes en el fichero
fich.write(buf);
} finally {
if (fich!=null) {
fich.close();
}
}
[/java]
Ejemplo de deserialización
[java]
// recupera del fichero un libro serializado
RandomAccessFile fich = null;
try {
// abre el fichero de acceso aleatorio
fich = new RandomAccessFile (nomFich, «r»);
// pone el puntero al principio
fich.seek(0L);
// Lee un array de bytes del fichero
byte[] bytes = new byte[(int) fich.length()];
fich.readFully(bytes);
// Deserializa el array de bytes
ObjectInputStream in = new ObjectInputStream(
new ByteArrayInputStream(bytes));
libro=(Libro) in.readObject();
in.close();
} finally {
if (fich!=null) {
fich.close();
}
}
[/java]