11. lectura y escritura de información

42
11. LECTURA Y ESCRITURA DE INFORMACIÓN

Transcript of 11. lectura y escritura de información

Page 1: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Page 2: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

1. Introducción

2. Formas de acceso a un fichero. Clases asociadas

3. Flujos o Streams. Tipos.

4. Formas de acceso a un fichero

5. Operaciones sobre ficheros

6. Clases para gestión de flujos de datos desde/hacia ficheros

Contenidos de la unidad

Page 3: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

En esta unidad se muestran algunas alternativas diferentes para trabajar con ficheros. Aunque no son todas las posibles, estas soluciones pueden considerarse una referencia dentro de las soluciones actuales respecto al almacenamiento de datos en ficheros.

Aunque las diferentes alternativas de acceso a ficheros mostradas en el capítulo se han trabajado desde la perspectiva de Java, la mayoría de entornos de programación dan soporte a estas mismas alternativas, aunque con otra sintaxis (por ejemplo, Microsoft .NET).

Las primeras secciones del capítulo se centran en el acceso a ficheros desde Java (flujos). Para comprender adecuadamente estos contenidos, es recomendable que el lector esté familiarizado con conceptos básicos de programación en Java.

1. Introducción

Page 4: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Un fichero o archivo es un conjunto de bits almacenado en un dispositivo, como por ejemplo un disco duro. Una de las ventajas de usar ficheros es que éstos no son volátiles. Los ficheros tienen un nombre y se ubican en directorios o carpetas, el nombre debe ser único en ese directorio. Por convención cuentan con diferentes extensiones que por lo general suelen ser tres letras (PDF, DOC, GIF, …) y nos permiten saber el tipo de archivo.

1. Introducción

Page 5: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Antes de ver las clases que leen y escriben datos en ficheros, vamos a manejar la clase File:

File es una clase dentro del paquete java.io

Representa un archivo o directorio dentro de un sistema de ficheros.

Un objeto de la clase File representa el nombre de un fichero o directorio.

Los métodos de File permiten obtener toda la información sobre las características del fichero o directorio.

2. Formas de acceso a un fichero. Clases asociadas.

Page 6: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

2. Formas de acceso a un fichero. Clases asociadas.

Page 7: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

El siguiente ejemplo muestra la lista de ficheros del directorio actual utilizando el método list() que devuelve un array de Strings con los nombres de los ficheros y directorios contenidos en el directorio asociado al objeto File:

import java.io.*;

public class VerDir {

public static void main (String[] args) {

System.out.println(“Ficheros en el directorio actual: “);

File f = new File(“.”);

String[] archivos = f.list();

for (int i = 0; i < archivos.length; i++) {

System.out.println(archivos[i]);

}

}

}

2. Formas de acceso a un fichero. Clases asociadas.

Page 8: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Algunos métodos importantes del objeto File son:

getName: devuelve el nombre del fichero o directorio.

getPath(): devuelve el camino relativo.

getAbsolutePath(): devuelve el camino absoluto del fichero/directorio.

canRead(): devuelve true si el fichero se puede leer.

canWrite(): devuelve true si el fichero se puede escribir.

length(): nos devuelve el tamaño del fichero en bytes.

createNewFile(): crea un nuevo fichero, vacío, asociado a File si y solo si no existe un fichero con dicho nombre.

2. Formas de acceso a un fichero. Clases asociadas.

Page 9: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

delete(): borra el fichero o directorio asociado al File.

exists(): devuelve true si el fichero/directorio existe.

getParent(): devuelve el nombre del directorio padre, o null si no existe.

isDirectory(): devuelve true si el objeto File corresponde a un directorio.

isFile(): devuelve true si el objeto File corresponde a un fichero normal.

mkdir(): crea un directorio con el nombre indicado en la creación del objeto File.

renameTo(File nuevonombre): renombra el fichero.

2. Formas de acceso a un fichero. Clases asociadas.

Page 10: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Ejemplos de uso de la clase Clase File.

Código para la creación de un objeto File con un fichero llamado libros.xmlFile f = new File(“proyecto\\libros.xml”);

Ejemplo de uso del objeto FileSystem.out.println (“Nombre: “ + f.getName());

System.out.println (“Directorio padre: “ + f.getParent());

System.out.println (“Ruta relativa: “ + f.getPath());

System.out.println (“Ruta absoluta: “ + f.getAbsolutePath());

2. Formas de acceso a un fichero. Clases asociadas.

Page 11: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

En Java, el acceso a ficheros es tratado como un flujo de información entre el programa y el fichero. Un flujo no es más que un objeto que hace de intermediario entre el programa y el origen o el destino de la información. Esta abstracción proporcionada por los flujos hace que los programadores, cuando quieren acceder a información, solo se tengan que preocupar por trabajar con los objetos que proporcionan el flujo, sin importar el origen o el destino concreto de donde vengan o vayan los datos.

Cualquier programa que tenga que obtener información de cualquier fuente necesita abrir un stream, igualmente si necesita enviar información abrirá un stream y se escribirá la información.

La vinculación de este stream con el dispositivo físico la hace el sistema de entrada/salida de Java.

3. Flujos o Streams. Tipos.

Page 12: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Tenemos dos tipos de flujos:

Flujos de bytes (8 bits): su uso está orientado a la lectura/escritura de datos binarios. Todas las clases de flujos de bytes descienden de las clases InputStream y OutputStream.

Flujos de caracteres (16 bits): realizan operaciones de entrada y salida de caracteres. El flujo de caracteres viene gobernado por las clases Reader y Writer.

3. Flujos o Streams. Tipos.

Page 13: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

3.1. Flujos de bytes.

Page 14: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

3.2. Flujos de caracteres.

Page 15: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Hay dos formas de acceso a la información almacenada en un fichero:

Acceso secuencial: los datos o registros se leen y se escriben en orden. Si se quiere acceder a un dato es necesario leer antes todos los anteriores. La escritura de datos se hace a partir del último dato escrito, no es posible hacer inserciones entre los datos que ya hay escritos.

Acceso directo o aleatorio: permite acceder directamente a un datos o registro sin necesidad de leer los anteriores y se puede acceder a la información en cualquier orden. Los datos están almacenados en registros de tamaño conocido y nos podemos mover de un registro a otro de forma aleatoria para leerlos o modificarlos.

4. Formas de acceso a un fichero.

Page 16: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

4. Formas de acceso a un fichero

Page 17: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Operaciones básicas sobre ficheros:

Creación del fichero.

Apertura del fichero.

Cierre del fichero

Lectura de los datos del fichero

Escritura de los datos en el fichero

5. Operaciones sobre ficheros.

Page 18: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

En los ficheros secuenciales los registros se insertan en orden cronológico. Si hay que añadir nuevos registros éstos se añaden a partir del final del fichero:

Consultas: .para consultar un determinado registro es necesario empezar la lectura desde el primer registro, y continuar leyendo secuencialmente hasta localizar el registro buscado.

Altas: las altas se realizan al final del último registro insertado, sólo se permite añadir datos al final del fichero.

Bajas: para dar de baja un registro es necesario hacer uso de un fichero auxiliar, leyendo todos los registros y copiarlos todos a excepción del que queramos dar de baja. Por último renombraremos el fichero auziliar dándole el nombre del fichero original.

Modificaciones: El proceso de modificaciones es similar al de bajas.

5.1. Operaciones sobre ficheros secuenciales.

Page 19: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Normalmente para posicionarnos en un registro es necesario aplicar una función de conversión que usualmente tiene que ver con el tamaño del registro y con la clave del mismo. Veamos cómo se realizan las operaciones típicas:

Consultas: .para consultar un determinado registro necesitamos saber su clave y aplicar la función de conversión para obtener la dirección del registro que queremos leer.

Altas: para insertar un registro necesitamos saber su clave, aplicar la función de conversión a la clave para obtener la dirección y escribir el registro en la posición devuelta. Si la posición está ocupada por otro registro, el registro se insertaría en la zona de excedentes.

Bajas: para realizar las bajas se suele utilizar un campo del registro a modo de switch que tenga el valor 1 cuando el registro exista y le damos el valor 0 para darle de baja. Físicamente el registro no desaparece del disco.

Modificaciones: Para modificar un registro hay que localizarlo, modificar los datos que nos interesen y reescribir el registro en esa posición.

5.2. Operaciones sobre ficheros aleatorios.

Page 20: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Clase FileReader

El flujo FileReader permite leer caracteres desde un fichero de modo secuencial. Esta clase hereda los métodos de la clase Reader. Los constructores principales son:

FileReader(String ruta)

FileReader(File fichero)

El fichero puede abrirse con una ruta de directorios o con un objeto de tipo File.

Al usar la clase FileReader se puede generar la excepción FileNotFoundException.

6.1. Clases para gestión de flujos de datos. Ficheros de Texto.

Page 21: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Clase FileReader

Los métodos que proporciona la clase FileReader son los siguientes:

int read(): lee un carácter y lo devuelve

int read(char[] buf): lee hasta buf.length caracteres de datos de una matriz de caracteres (buf). Los caracteres leídos se van almacenando en buf.

int read(char[] buf. Int desplazamiento, int n): lee hasta n caracteres de datos de la matriz buf comenzando por buf[desplazamiento] y devuelve el número leído de caracteres.

6.1. Clases para gestión de flujos de datos. Ficheros de Texto.

Page 22: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Clase FileReader import java.io.*;

public class LeerFichTexto {

public static void main (String[] args) throws IOException {

//declarar fichero

File fichero = new File(“C:\\EJERCICIOS\\LeerFicheroTexto.java”);

FileReader fic = new FileReader(fichero); //Crear el flujo de entrada

int i;

while ((i = fic.read()) != -1) //Se va leyendo un carácter

System.out.println((char) i);

fic.close(); //cerrar fichero

}

}

6.1. Clases para gestión de flujos de datos. Ficheros de Texto.

Page 23: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

FileReader no contiene métodos que nos permitan leer líneas completas, pero BufferedReader sí; dispone del método readLine() que lee una línea del fichero y la devuelve, o devuelve null si no hay nada que leer o llegamos al final del fichero. Para construir un BufferedReader necesitamos la clase FileReader:

BufferedReader fichero = new BufferedReader(new FileReader(NombreFichero));

6.1. Clases para gestión de flujos de datos. Ficheros de Texto.

Page 24: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Clase FileWriter

El flujo FileWriter permite escribir caracteres en un fichero de modo secuencial. Esta clase hereda los métodos necesarios para ello de la clase Writer. Los constructores principales son:

FileWriter (String ruta, boolean añadir)

FileWriter(File fichero)

El parámetro ruta indica la localización del archivo en el sistema operativo. El parámetro añadir igual a true indica que el fichero se usa para añadir datos a un fichero ya existente.

6.1. Clases para gestión de flujos de datos. Ficheros de Texto.

Page 25: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Clase FileWriter

Los métodos que proporciona la clase FileWriter son:

void write(int c): escribe un carácter

void write(char[] buf): escribe un array de caracteres

void write(char[] buf, int desplazamiento, int n): escribe n caracteres de datos en la matriz buf y comenzando por buf[desplazamiento].

void write(String str): escribe una cadena de caracteres

append(char c): añade un carácter a un fichero

Estos métodos también pueden lanzar la excepción IOException.

6.1. Clases para gestión de flujos de datos. Ficheros de Texto.

Page 26: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

La clase BufferedWriter también deriva de la clase Writer. Esta clase añade un buffer para realizar una escritura eficiente de caracteres. Para construir un BufferedWriter necesitamos un FileWriter:

BufferedWriter fichero = new BufferedWriter(new FileWriter(NombreFichero));

La clase PrintWriter posee los métodos print(String) y println(String) para escribir en un fichero. Ambos reciben un String y los escriben en un fichero, el segundo método salta de línea. Para escribir un PrintWriter necesitamos la clase FileWriter:

PrintWriter fichero = new PrintWriter(new FileWriter(NombreFichero));

6.1. Clases para gestión de flujos de datos. Ficheros de Texto.

Page 27: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Los ficheros binarios almacenan secuencias de dígitos binarios que no son legibles directamente por el usuario como ocurría con los ficheros de texto. Tienen la ventaja de que ocupan menos espacio en el disco. En Java, las dos clases que nos permiten trabajar con ficheros binarios son FileInputStream y FileOutputStream.

6.2. Clases para gestión de flujos de datos. Ficheros binarios.

Page 28: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Clase FileInputStream

El flujo FileInputStream permite leer bytes en un fichero de manera secuencial. Sus constructores tienen los mismos parámetros que los mostrados para FileReader.

Los métodos que proporciona la clase FileInputStream son similares a los vistos para FileReader. Estos métodos devuelven el número de bytes leídos o -1 si se ha llegado al final del fichero:

int read()

int read(byte[] b)

int read(byte[] b, int desplazamiento, int n)

6.2. Clases para gestión de flujos de datos. Ficheros binarios.

Page 29: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Clase FileOutpuStream

El flujo FileOutputStream permite escribir bytes en un fichero de manera secuencial. Sus constructores tienen los mismos parámetros que los mostrados para FileWriter: el fichero puede ser abierto vacío o listo para añadirle datos a los que ya contenga.

Los métodos que proporciona la clase FileOutputStream son:

void write(int b)

void write(byte[] b)

void write(byte[] b, int desplazamiento, int n)

6.2. Clases para gestión de flujos de datos. Ficheros binarios.

Page 30: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Para leer y escribir datos de tipos primitivos: int, float, long,… usaremos las clases DataInputStream y DataOutputStream. Estas clases además de los métodos read() y write() proporcionan otros métodos de lectura y escritura:

6.2. Clases para gestión de flujos de datos. Ficheros binarios.

MÉTODOS PARA LECTURA MÉTODOS PARA ESCRITURAboolean readBoolean();byte readByte();int readUnsignedByte();int readUnsignedShort();short readShort();char readChar();int readInt();long readLong();float readFloat();double readDouble();String readUTF();

void writeBoolean(boolean v);void writeByte(int v);void writeUnsignedByte(String s);void writeShort(int v);void writeChars(String s);void writeChar(int v);void writeInt(int v);void writeLong(long v);void writeFloat(float v);void writeDouble(doublé v);void writeUTF(String str);

Page 31: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Para abrir un objeto DataInputStream lo haremos de la siguiente forma:

File fichero = new File(“nombrefichero”);

FileInputStream filein = new FileInputStream(fichero);

DataInputStream dataIS = new DataInputStream(filein);

Para abrir un objeto DataOutputStream lo haremos de la siguiente forma:

File fichero = new File(“nombrefichero”);

FileOutputStream fileout = new FileOutputStream(fichero);

DataOutputStream dataOS = new DataOutputStream(fileout);

6.2. Clases para gestión de flujos de datos. Ficheros binarios.

Page 32: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Java dispone de la clase RandomAccessFile que contiene métodos para acceder al contenido de un fichero binario de forma aleatoria y para posicionarnos en una posición concreta del mismo.

Disponemos de dos posibilidades para crear el fichero de acceso aleatorio:

Escribiendo el nombre del fichero:

fichero = new RandomAccessFile(String nombre, String modoAcceso);

Con un objeto File:

fichero = new RandomAccessFile(File fich, String modoAcceso);

El argumento modoAcceso puede ser “r” para solo lectura o “rw” para lectura y escritura.

La clase RandomAccessFile maneja un puntero que indica la posición actual en el fichero. Cuando el fichero se crea el puntero se coloca en 0. Las sucesivas llamadas a los métodos read() y write() ajustan el puntero según la cantidad de bytes leídos o escritos.

6.3. Ficheros de acceso aleatorio.

Page 33: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Los métodos más importantes son:

long getFilePointer(): devuelve la posición actual del puntero del fichero.

void seek(long posicion): coloca el puntero del fichero en una posición determinada desde el comienzo del mismo.

long length(): devuelve el tamaño del fichero en bytes. La posición length() marca el final del fichero.

int skipBytes(int desplazamiento): desplaza el puntero desde la posición actual el número de bytes indicados en desplazamiento.

6.3. Ficheros de acceso aleatorio.

Page 34: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

A continuación vamos a ver un ejemplo de un programa que escribe un fichero aleatorio con los datos de empleados, teniendo en cuenta las siguientes consideraciones:

Los datos a insertar son: apellido, departamento y salario.

Los datos se van introduciendo de forma secuencial, no se usará el método seek().

Por cada empleado también se insertará un identificador que coincidirá con el índice +1 con el que se recorren los arrays

La longitud del registro de cada empleado es la misma (36 bytes) y los tipos que se insertan y su tamaño en bytes es el siguiente:

Identificador: es un entero, ocupa 4 bytes

Apellido: cadena de 10 caracteres. Cada carácter Unicode ocupa 2 bytes, luego el apellido ocupa 20 bytes

Departamento: es un entero, ocupa 4 bytes.

Salario: es un doublé, ocupa 8 bytes.

Nota: Tamaño de otros tipos: short (2 bytes), byte (1 byte), long (8 bytes), boolean (1 bit), float (4 bytes), etc.

6.3. Ficheros de acceso aleatorio.

Page 35: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

6.3. Ficheros de acceso aleatorio.

Page 36: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

6.3. Ficheros de acceso aleatorio.

Page 37: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

Para insertar un nuevo registro aplicamos la función de conversión al identificador para calcular la posición. El siguiente código inserta un empleado con identificador 20. Se ha de calcular la posición donde irá el registro dentro del fichero (identificador -1) * 36 bytes.

6.3. Ficheros de acceso aleatorio.

Page 38: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

OBJETOS SERIALIZABLES

Con lo visto hasta ahora, si, por ejemplo, tenemos un objeto de tipo empleado con varios atributos (nombre, dirección, salario, departamento,…) y queremos guardarlo en un fichero, tendríamos que guardar cada atributo que forma parte del objeto por separado. Java nos permite guardar objetos en ficheros binarios; para poder hacerlo, el objeto tiene que implementar la interfaz Serializable que dispone de una serie de métodos con los que podremos guardar y leer objetos en ficheros binarios. Los más importantes son:

void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException: para leer un objeto.

void writeObject(java.io.ObjecOutputStream stream) throws IOException: para escribir un objeto.

Para leer y escribir objetos serializables a un stream se utilizan las clases Java ObjectInputStream y ObjectOutputStream respectivamente.

6.4. Clases para gestión de flujos de datos. Ficheros binarios. Objetos serializables.

Page 39: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

OBJETOS SERIALIZABLES

A continuación se muestra la clase Persona que implementa la interfaz Serializable y que utilizaremos para escribir y leer objetos en un fichero binario.

6.4. Clases para gestión de flujos de datos. Ficheros binarios. Objetos serializables.

Page 40: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

OBJETOS SERIALIZABLES

El siguiente ejemplo escribe objetos Persona en un fichero. Necesitamos crear un flujo de salida FileOutputStream y a continuación se crea el flujo de salida ObjectOutputStream, que es el que procesa los datos.

El método writeObject() escribe los objetos al flujo de salida y los guarda en un fichero.

6.4. Clases para gestión de flujos de datos. Ficheros binarios. Objetos serializables.

Page 41: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

OBJETOS SERIALIZABLES

Para leer objetos Persona de un fichero necesitamos el flujo de entrada a disco FileInputStream y a continuación crear el flujo de entrada ObjectInputStream, que es el que procesa los datos.

El método readObject() lee los objetos del flujo de entrada y puede lanzar la excepción ClassNotFoundException.

6.4. Clases para gestión de flujos de datos. Ficheros binarios. Objetos serializables.

Page 42: 11. lectura y escritura de información

11. LECTURA Y ESCRITURA DE INFORMACIÓN

OBJETOS SERIALIZABLES

ObjectOutputStream puede darnos algunos problemas, por ejemplo, si escribimos datos en el fichero y lo cerramos. Después volvemos a abrirlo para añadir datos y entonces se escribe una nueva cabecera al final de los objetos introducidos anteriormente y después se van añadiendo el resto de los datos. Esto origina el problema que al leer el fichero se produzca la excepción StreamCorruptedException.

6.4. Clases para gestión de flujos de datos. Ficheros binarios. Objetos serializables.