Saltar al contenido

RandomAccessFile en java. Archivos de acceso aleatorio.

Si traducimos el nombre de la clase RandomAccessFile al español, estaremos hablando de archivos de acceso aleatorio. Es decir, con esta clase, podemos acceder de forma aleatoria a un archivo o fichero. Además, los archivos de este tipo, se pueden leer, o bien leer y escribir a la vez.

Por otra parte, estos archivos no son stream y se numeran por un índice que empieza por cero. Este índice, se llama puntero o cursor de lectura/escritura e indica la posición a partir de la cual se empezará a leer o escribir en el archivo. Es importante indicar que la información almacenada en este tipo de archivos, se guarda en forma de bytes. Esto quiere decir que este la clase RandomAccessFile trata del archivo como un array o arreglo de bytes.

Como determinar la longitud de un dato usando la clase RandomAccessFile.

Para poder determinar cuanto ocupa cada registro en un archivo de acceso aleatorio, debemos tener en cuenta los bytes de cada uno de los datos. De esta forma, dependiendo del tipo de dato que queramos almacenar, cada registro ocupará un tamaño u otro. A continuación, mostramos una imagen con el tamaño en bytes que ocupa tipo de dato.

RandomAccessFile en java. Tamaño de los tipos de datos.
Tamaño de los tipos de datos en java para archivos de acceso aleatorio

Hay que tener en cuenta que los datos tipo String (tipo texto), en java son considerados como objetos. Por este motivo, un dato tipo String, se considera un array de caracteres tipo char. Esto quiere decir que la información de tipo String, ocupará dos bytes por cada carácter tipo char. A esta información, se le deben sumar los bytes ocupados por los espacios en blanco y los saltos de línea.

Uso de objetos en ficheros de acceso aleatorio.

Para poder almacenar objetos en archivos aleatorios con la clase RandomAccessFile, es necesario convertirlos a un array de bytes. A este proceso se le llama serialización. Esto se consigue indicándole a la clase de java, que debe implementar la interfaz serializable.

Por otro lado, para poder leer un fichero que contiene objetos creados en java, debemos deserializar dicho objeto. Esto lo conseguimos con la clase ByteArrayOutputStream().

A continuación, empezaremos con el desarrollo un ejemplo para explicar el uso de la clase RandomAccessFile. Para este ejemplo, como ocurre con el ejemplo anterior, haremos uso de las clases contenidas en el paquete “java.io” correspondientes a la API de java. También usaremos una clase llamada Persona con sus atributos y sus métodos.

package archivosaleatorios;

import java.io.Serializable;

public class Persona implements Serializable {
    /* Declaramos los atributos o características de nuestro objeto persona:
    DNI, nombre, apellidos, edad y sexo*/
    private int id;
    private String dni;
    private String nombre;
    private String apellidos;
    private int edad;
    private char sexo;
    
    Persona(){
        
    }
    
    // Creamos los métos setter (para establecer valores al objeto
    // y getter que devolverá el valor de cada uno de los atributos del objeto

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }
    
    public void setDni(String dni) {
        this.dni = dni;
    }
    
    public String getDni() {
        return dni;
    }

    public void setNombre(String nombre) {
            this.nombre = nombre;   
    }

    public String getNombre() {
        return nombre;
    }

    public void setApellidos(String apellidos) {
        this.apellidos = apellidos;
    }
   
    public String getApellidos() {
        return apellidos;
    }

    public void setEdad(int edad) {
        this.edad = edad;
    }

    public int getEdad() {
        return edad;
    }

    public void setSexo(char sexo) {
        this.sexo = sexo;
    }
 
    public char getSexo() {
        return sexo;
    }
    
    /* Sobreescribimos el método toString() de la clase Object. Esto lo hacemos
    para devolver todos los datos de la persona en cuestión.*/

    @Override
    public String toString() {
        return this.getDni() + ", " + this.getNombre() + ", " 
                + this.getApellidos () + ", " + this.getEdad()+ ", " 
                + this.getSexo() + ".\n";
    } 
}

Almacenar objetos en archivos de acceso aleatorio. Escritura con RandomAccessFile.

Para este ejemplo crearemos las siguientes variables dentro de la clase que contiene el método main. Todas ellas las crearemos a nivel de clase para que puedan utilizarse en cualquier método de la clase principal.

package archivosaleatorios;

// Importamos las clases del paquete java.io
// donde encontramos la clase RandomAccessFile
import java.io.*;
import java.util.*;

public class ArchivosAleatorios {

    // Creamos una variable para leer los datos que se piden por teclado
    private static Scanner lectura = new Scanner(System.in);
    
    /* Variables para almacenar los datos de cada persona que se introduce
    por teclado. El sexo será 'M' para masculino y 'F' para femenino. Además
    los datos, tendrán una longitud fija máxima*/
    
    private static String idPersona=""; // Almacena un id unico de la persona
    private static int id=0; // 4 bytes
    private static String dniPersona=""; // 9 por 2 bytes = 18 bytes
    private static String nombrePersona=""; // 20 por 2 bytes = 40 bytes
    private static String apellidoPersona=""; // 20 por 2 bytes = 40 bytes
    private static String edadPersona=""; // Contendrá la edad en texto.
    private static int edadEntera=0; // 4 bytes
    private static char sexoPersona='M'; // 1 caracter x 2 bytes = 2 bytes
    // Almacena los bytes que ocupa un registro como máximo.
    private static int longitudRegistro =110;
    
    // Variable objeto de tipo Persona que almacena un nuevo objeto Persona.
    private static Persona nuevaPersona = new Persona();
    
    // Creamos una variable objeto de tipo RandomAccessFile.
    private static RandomAccessFile archivo=null;
    
    /* Variables necesarias para serializar los objetos de tipo Persona
    
    Varaibles para poder escribir en el fichero*/
    private static byte []array=null;
    private static ByteArrayOutputStream escribir=null;
    private static ObjectOutputStream salida=null;
    
    // Variables para poder leer el fichero
    private static ByteArrayInputStream leer=null;
    private static ObjectInputStream entrada = null;

En segundo lugar, crearemos un método para pedir los datos por teclado al usuario.

Método que pide los datos al usuario.

// Metodo que pide los datos de la persona por teclado.
    private static boolean pedirDatos(){
        
        // Pedimos al usuario cada uno de los datos de la persona
        do{
            System.out.println("Introduce el ID de la persona. Este ID "
                    + "debe ser mayor que cero: ");
            idPersona=lectura.nextLine();
            
            // Intentamos pasarlo a número
            try {
                id=Integer.parseInt(idPersona);
            } catch (NumberFormatException e) {
                System.out.println("Debes introducir un ID mayor que cero");
            }
        }while(idPersona.isEmpty() || id<=0);
        
        do{
            System.out.println("Introduce el DNI de la persona: ");
            dniPersona=lectura.nextLine();
        }while(dniPersona.isEmpty() || dniPersona.length() !=9);
        
        do{
            System.out.println("Introduce el nombre de la persona: ");
            nombrePersona=lectura.nextLine();
        }while(nombrePersona.isEmpty() || nombrePersona.length()>20);
        
        do{
            System.out.println("Introduce un apellido de la persona: ");
            apellidoPersona=lectura.nextLine();
        }while(apellidoPersona.isEmpty() || apellidoPersona.length()>20);
        
        do{
            System.out.println("Introduce la edad de la persona: ");
            edadPersona=lectura.nextLine();
            try {
                edadEntera=Integer.parseInt(edadPersona);
            } catch (NumberFormatException e) {
                System.out.println("Debes introducir un número entero");
            }
        }while(edadPersona.isEmpty() || edadEntera<=0);
        
        do{
            System.out.println("Introduce el sexo de la persona. 'M' para "
                    + "masculino o 'F' para femenino. (Sin las comillas): ");
            sexoPersona=lectura.next().charAt(0);
        }while(sexoPersona !='M' && sexoPersona !='F');
        
        // Pasamos los datos al objeto Persona
        nuevaPersona.setId(id);
        nuevaPersona.setDni(dniPersona);
        nuevaPersona.setNombre(nombrePersona);
        nuevaPersona.setApellidos(apellidoPersona);
        nuevaPersona.setEdad(edadEntera);
        nuevaPersona.setSexo(sexoPersona);
        // Devolvemos true
        return true;
    }

A continuación, crearemos el método para escribir datos en el archivo o fichero.

Método para escribir en un archivo con la clase RandomAccessFile.

/* Método para escribir en un archivo de acceso aleatorio con la clase
    RandomAccessFile.*/
    
    private static void escribirEnArchivoAleatorio(){
        try {
            /* Creamos o abrimos un nuevo archivo. En este caso:
            El primer parámetro hace referencia a la ruta del archivo.
            El segundo parametro es el siguiente:
            - r - read. Solo lectura.
            - rw - read/wirte. Lectura y escritura */
            archivo = new RandomAccessFile("src/Registrar_personas.txt", "rw");
            
            // Ponemos el puntero al final del archivo
            archivo.seek(archivo.length());
            
            // Serializamos el objeto Persona
            // Lo convertimos en una secuencia de bytees.
            escribir= new ByteArrayOutputStream();
            salida = new ObjectOutputStream(escribir);
            salida.writeObject(nuevaPersona.toString());
            
            // Cerramos el objeto.
            salida.close();
            
            // obtenemos los bytes del libro serializado
            array = escribir.toByteArray();
            
            // Escribimos los bytes en el archivo.
            archivo.write(array);
            
            // Cerramos el archivo
            archivo.close();
        } catch (Exception e) {
            System.out.println("No se puede escribir en el archivo" 
            + e.getMessage());
        }
    }

Para poder leer un archivo de acceso aleatorio con la clase RandomAccessFile, crearemos un método para a parte de estos dos dentro de la misma clase.

Función para leer archivos de acceso aleatorio.

Este método se desarrolla entre las líneas 142 a la 181.

// Metodo para leer el archivo de acceso aleatorio
    private static void leerArchivoAleatorio(){
        try {
            /* Creamos o abrimos un nuevo archivo. Este archivo lo crearemos
            dentro de la carpeta src de nuestro proyecto. Además debemos tener
            en cuenta que el constructor de la clase RandomAccessFile recibe
            2 parámetros:
            El primero hace referencia a la ruta del archivo.
            El segundo hace referencia al modo de apertura del archivo:
            - r - read. Solo lectura.
            - rw - read/wirte. Lectura y escritura */
            archivo = new RandomAccessFile("src/Registrar_personas.txt", "r");
            
            // Nos posicionamos al principio del fichero
            archivo.seek(0);
            
            // Almacenamos los bytes del fichero en un array de bytes
            array = new byte[(int)archivo.length()];
            
            // Leemos todos los bytes del fichero
            archivo.readFully(array);
            
            // Convertimos ese array de bytes en un objeto.
            leer = new ByteArrayInputStream(array);
            entrada = new ObjectInputStream(leer);
            
            /* Hacemos una conversion de lo que lee el ObjectInputStream
            a un objeto de tipo Persona y lo almacenamos
            en la variable objeto nuevaPersona*/
            nuevaPersona=(Persona) entrada.readObject();
            System.out.println(nuevaPersona);
            // Cerramos el objeto ObjectInputStream
            entrada.close();
            
            
        } catch (Exception e) {
            System.out.println("No se puede leer el archivo" 
            + e.getMessage());
        }
    }

Finalmente para que el programa funcione, debemos llamar a ambos métodos desde el main.

Metodo main del programa.

El método main serán las lineas desde la 183 a la línea 203, la cual incluye la llave de cierre de la clase que estamos trabajando.

public static void main(String[] args) {
        
        /*Si a la hora de pedir los datos devuelve true, es que los datos
        se han introducido de forma correcta.*/
        if(pedirDatos()){
            
            // Si los datos son correctos, intentamos...
            try {
                escribirEnArchivoAleatorio();
                // Escribimos en el archivo.
                //escribirEnArchivoAleatorio(nuevaPersona);
            } catch (Exception e) {
                System.out.println("No se ha podido registrar la persona.");
            }
        }
        
        /* Si se quiere leer el archivo,
        solo debemos llamarlo desde este método, de la siguiente manera*/
        leerArchivoAleatorio();
    }
}

DESCARGAR PROYECTO