Sunteți pe pagina 1din 46

Tema 4. Entrada y salida.

Flujos y
ficheros.
Programación (PRG)
Curso 2018/19

Departamento de Sistemas Informáticos y Computación


Índice y bibliografía
1. Introducción:
Flujos: InputStream, OutputStream
Ficheros
2. Acceso al sistema de archivos
– La clase File
3. Ficheros de texto
– Escritura: la clase Printwriter. Métodos y excepciones
– Lectura: la clase Scanner. Métodos y excepciones
4. Cierre de ficheros
5. Ficheros binarios. Ficheros binarios de acceso secuencial
– Escritura en ficheros binarios secuenciales: la clase ObjectOutputStream
– Lectura en ficheros binarios secuenciales: la clase ObjectInputStream
– Excepciones en las clases ObjectOutputStream, ObjectInputStream
– Determinación de final de fichero
6. Resumen y recomendaciones al trabajar con ficheros

• N. Prieto y otros. Empezar a programar usando Java. Editorial UPV, 2012.


Capítulos 6 y 15
PRG. ETSINF. UPV - Curso 2018/19 2
Introducción: flujos
• En Java la entrada/salida de datos se realiza utilizando flujos (streams):
secuencias de información que tienen asociada una fuente o entrada (flujo de
entrada) o un destino o salida (flujo de salida).
• Las fuentes y los destinos pueden ser dispositivos de diferentes tipos: ficheros
del sistema, terminales, ...

flujo de entrada:
fuente + secuencia de información
Fuente

Programa

flujo de salida:
destino + secuencia de información destino

PRG. ETSINF. UPV - Curso 2018/19 3


Introducción: flujos
• Los flujos de entrada en Java son de la clase InputStream (y sus derivadas,
como FileInputStream : flujos cuya fuente es un fichero).
• Los flujos de salida en Java son de la clase OutputStream (y sus derivadas,
como FileOutputStream: flujos cuyo destino es un fichero).

Programa
InputStream
OutputStream

PRG. ETSINF. UPV - Curso 2018/19 4


Introducción: ficheros
• Un fichero es un conjunto de bits guardado en un dispositivo secundario
de almacenamiento (disco duro, USB stick, etc.).
– Permite almacenar los datos existentes en memoria de un programa para ser
utilizadas posteriormente (por este u otro programa).

• Características principales :
– Nombre (p.e. fichero.txt).
– Ruta en el dispositivo de almacenamiento
(p.e. /home/lucas/docs/file.txt).
– Tamaño, típicamente expresado en bytes (Kb, Mbytes, Gbytes, etc.).

• Características adicionales:
– Permisos de acceso (dependientes del sistema de ficheros).
– Fecha de la última modificación.
– …

• Acciones principales sobre ficheros:


– Abrir, leer, cerrar.
– Abrir, escribir, cerrar.

PRG. ETSINF. UPV - Curso 2018/19 5


Introducción: ficheros
• En general, se distinguen dos tipos de ficheros:

Ficheros de texto Ficheros binarios


• Secuencia de caracteres. • Secuencia de bytes interpretables
• Interpretable por un humano. como valores (primitivos u objetos).
• Generalmente portable (legible • No interpretable por un humano.
en diferentes equipos). • Generalmente no portable (se debe
• Escritura/lectura menos eficiente leer en el mismo tipo de ordenador
que para ficheros binarios. y con el mismo lenguaje de
• Requiere mayor tamaño que un programación con el que se creó).
fichero binario para representar • Escritura/lectura eficiente.
la misma información. • Almacenamiento eficiente de la
Ejemplo. Un entero de 10 dígitos información.
en un fichero de texto ocupa 10 Ejemplo. Un entero de 10 dígitos en
bytes (suponiendo codificación un fichero binario ocupa 4 bytes.
ASCII de 1 byte / carácter).

PRG. ETSINF. UPV - Curso 2018/19 6


Introducción: ficheros
• En general:
Leer de un fichero en Java requiere definir un flujo de entrada cuya fuente
sea el fichero (un FileInputStream) .
Escribir en un fichero en Java requiere definir un flujo de salida cuyo
destino sea el fichero (un FileOutputStream).

Programa
FileInputStream
FileOutputStream

• Leer y escribir directamente de estos flujos es farragoso (sus métodos leen y


escriben byte a byte).
• Java proporciona otras clases que facilitan el manejo de estos flujos,
especializadas al tipo de ficheros (de texto, binarios).

PRG. ETSINF. UPV - Curso 2018/19 7


Introducción: ficheros
• En Java, los ficheros binarios son independientes de la plataforma
(pueden leerse usando Java en plataformas diferentes).
• Java permite:
1. Interactuar con el sistema de archivos independientemente de su tipo
(FAT32, NTFS, EXT3, etc.) y del S.O. (Windows, Linux, MAC, etc.).
2. Crear, leer y escribir en ficheros de texto (compuestos tanto por
representaciones en caracteres de los tipos primitivos como por
Strings).
3. Producir y leer ficheros binarios compuestos por datos de tipos
primitivos y Strings.
4. Trabajar con ficheros binarios formados por objetos.
5. Manipular ficheros binarios a bajo nivel (E/S a nivel de bytes).

• Este tema se centra en los puntos 1 al 4.

PRG. ETSINF. UPV - Curso 2018/19 8


Acceso al sistema de archivos: clase File
• Clase File (en java.io):
– Permite representar en Java las rutas de los ficheros y directorios del sistema.

Método Descripción
File(String pathname) Crea un nuevo objeto de tipo File a partir de la ruta
boolean delete() Borra el fichero/directorio. Retorna true si se ha podido
borrar, false en caso contrario.
boolean exists() Indica si el fichero/directorio existe
String getName() Retorna el nombre del fichero/directorio (sin la ruta)
String getParent() Retorna la ruta del padre, si es conocida.
long length() Retorna la longitud del fichero. No válido para directorios
File[] listFiles() Retorna un array con la lista de ficheros en el directorio
boolean isDirectory() Indica si se trata de un directorio
. . .

Crear un objeto File no modifica el sistema de archivos; solamente se dan cambios si


se ejecutan ciertas operaciones (por ejemplo, un delete con éxito, mkdir, etc ... ).
PRG. ETSINF. UPV - Curso 2018/19 9
Acceso al sistema de archivos: clase File
• Ejemplo de uso de la clase File.
import java.io.File;
public class TestFile {
public static void main(String[] args) {
File f = new File("/home/pcasals/file.txt");
System.out.println("nombre: " + f.getName());
System.out.println("padre: " + f.getParent());
// f solo es un "descriptor" de fichero (describe una
// ruta en el sistema de archivos)

if (f.exists()) { // Existe un fichero con dicha ruta


System.out.println("El fichero existe");
System.out.println("length(): " + f.length());
} else { System.err.println("El fichero NO existe"); }

}
}

• getName() obtendría "file.txt"


• getParent() obtendría "/home/pcasals".
PRG. ETSINF. UPV - Curso 2018/19 10
Ficheros de texto. Escritura: Printwriter
• La forma más cómoda de generar y escribir ficheros de texto es mediante
la clase PrintWriter (del paquete java.io):
– Proporciona los métodos que facilitan la escritura como texto de valores de tipos
primitivos y String .
Método Descripción
PrintWriter(OutputStream out) Crea un PrintWriter a partir del flujo out
PrintWriter(File file) Crea un PrintWriter a partir del File. Puede
lanzar la excepción FileNotFoundException
PrintWriter(String filename) Crea un PrintWriter a partir del nombre del
fichero. Puede lanzar FileNotFoundException
void print(float f) Escribe un float
void println(float f) Escribe un float y añade un cambio de línea
void print(boolean b) Escribe un boolean
void print(String s) Escribe un String sin cambio de línea
void close() Cierra el Printwriter y libera los recursos que
estuvieran asociados a él.
...

PRG. ETSINF. UPV - Curso 2018/19 11


Ficheros de texto. Escritura: Printwriter
• Ejemplo.

PrintWriter pw = new PrintWriter(new File("/tmp/fSalida.txt"));

• Al crear un PrintWriter:
Si el fichero no existe se crea uno nuevo, si existe se trunca su tamaño a
cero.
Si se diera algún error (no se puede crear un nuevo fichero en el sistema,
falla la apertura del fichero, ...) se lanza la excepción comprobada
FileNotFoundException.

• Cuando se ha creado con éxito un PrintWriter y ya no se necesite usar


más en la aplicación, se puede cerrar con el método close().

PRG. ETSINF. UPV - Curso 2018/19 12


Ficheros de texto. Escritura: Printwriter
• Ejemplo de programa que usa PrintWriter.
import java.io.File;
import java.io.PrintWriter;
import java.io.FileNotFoundException;
public class TestPrintWriter {
public static void main(String[] args) {
String fichero = "file2.txt";
try {
PrintWriter pw = new PrintWriter(new File(fichero));
pw.print("Hola a ");
pw.println("todos.");
pw.println(15 * 2 / 3);
pw.close();
} catch (FileNotFoundException e) {
System.err.println("Problemas al abrir el fichero "
+ fichero);
}
}
}

• El programa escribe en el fichero:


Hola a todos.
10 PRG. ETSINF. UPV - Curso 2018/19 13
Ficheros de texto. Lectura: Scanner
• La clase Scanner (del paquete java.util) facilita la lectura procedente de
diversos tipos de fuentes.
• Los diferentes métodos constructores permiten crear un Scanner cuya fuente de
caracteres sea de diferentes clases:

Métodos constructores Descripción


Scanner(File source) Crea un Scanner a partir de un File. Lanza la excepción
FileNotFoundException si el fichero no existe o no
se puede abrir.
Scanner(String source) Crea un Scanner para leer datos a partir del String
Scanner(InputStream source) Crea un Scanner a partir de un InputStream, (la
entrada estándar System.in es un InputStream).
...

• Contiene otros métodos, para configurar el Scanner, cerrarlo, etc ...,


void close() Cierra el Scanner y libera los recursos que estuvieran
asociados a él.
void useLocale(Locale locale) Fija la configuración al modo de trabajo local indicado.
...
PRG. ETSINF. UPV - Curso 2018/19 14
Ficheros de texto. Lectura: Scanner
• Ejemplos.

Scanner teclado = new Scanner(System.in).useLocale(Locale.US);

Scanner texto = new Scanner("Texto a escanear. \n. Separado en dos líneas.");

Scanner fichero = new Scanner(new File("/tmp/fEntrada.txt"));

• Si el fichero especificado no está accessible en el sistema de archivos, el


constructor Scanner(File) lanza la excepción FileNotFoundException
(comprobada).

• Cuando se ha creado con éxito un Scanner y ya no se necesite usar más en la


aplicación, se puede cerrar con el método close().

PRG. ETSINF. UPV - Curso 2018/19 15


Ficheros de texto. Lectura: Scanner
• Los principales métodos que se pueden aplicar a un Scanner son los de lectura.
• En un Scanner los datos de la fuente o entrada se consideran divididos en tokens:
palabras o secuencias de caracteres entre separadores (blancos Java por defecto), que
se procesan uno a uno.
• Contiene métodos que permiten traducir los tokens a valores de diversos tipos primitivos:

Métodos de lectura Descripción


boolean hasNext() Retorna true si queda al menos un token en la entrada
...
boolean hasNextInt() Retorna true si el siguiente token de la entrada es un entero
int nextInt() Retorna el siguiente token de la entrada como un int.
...
String next() Retorna el siguiente token de la entrada
String nextLine() Retorna el resto de la línea
...

... y los análogos para boolean, byte, short, long, float, double ...
PRG. ETSINF. UPV - Curso 2018/19 16
Ficheros de texto. Lectura: Scanner
• Ejemplo.
Un Scanner contiene
un buffer de memoria
(char[]) en donde
dispone los caracteres
que le llegan de la
fuente

PRG. ETSINF. UPV - Curso 2018/19 17


Ficheros de texto. Lectura: Scanner
• Todos los métodos de lectura, como next(), nextInt(), nextDouble(),
etc, lanzan la excepción NoSuchElementException si la fuente está
agotada.

• Además, los métodos de lectura de tipo numérico o boolean nextInt(),


nextDouble(), etc, antes de extraer el token del Scanner lo examinan para
ver si se ajusta al formato correspondiente (o compatible):
– Si se ajusta, calculan el valor correspondiente y lo retornan como resultado. Los
caracteres del token se desechan.
– Si no se ajusta, se lanza la excepción InputMismatchException, pero los
caracteres del token examinado no se desechan: si se captura la excepción, los
caracteres siguen estando a disposición para la siguiente lectura.

PRG. ETSINF. UPV - Curso 2018/19 18


Ficheros de texto. Lectura: Scanner
• Ejemplo.
String fuente = " hola 1\n 2\n tres";
Scanner entrada = new Scanner(fuente).useLocale(Locale.US);
token

' ' 'h' 'o' 'l' 'a' ' ' '1' '\n' ' ' '2' '\n' ...

String palabra1 = entrada.next(); // palabra1 es "hola"

' ' 'h' 'o' 'l' 'a' ' ' '1' '\n' ' ' '2' '\n' ...

int n1 = entrada.nextInt(); // n1 es 1

' ' 'h' 'o' 'l' 'a' ' ' '1' '\n' ' ' '2' '\n' ...

double x1 = entrada.nextDouble(); // x1 es 2.0


... '1' '\n' ' ' '2' '\n' 't' 'r ' 'e' 's' ...

double x2 = entrada.nextDouble(); // InputMismatchException

... '1' '\n' ' ' '2' '\n' ' ' 't' 'r ' 'e' 's' ...
El token erróneo no se extrae del Scanner
PRG. ETSINF. UPV - Curso 2018/19 19
Ficheros de texto. Lectura: Scanner
• Ejemplo. Se intenta leer un real permitiendo que el usuario lo escriba en notación local o
americana. Si se lee un token erróneo en una notación, se intenta una segunda lectura en la otra.

Scanner teclado = new Scanner(System.in); // Configuracion local


double x = 0.0; boolena lecturaCorrecta = false; String basura = "";
System.out.print("Teclee un real: ");
try {
x = teclado.nextDouble(); lecturaCorrecta = true;
} catch (InputMismatchException e) {
// Por si acaso el usuario no estuviese usando notacion
// local, se cambia el modo de trabajo del Scanner:
teclado.useLocale(Locale.US);
// y se insiste en leer el MISMO token:
try {
x = teclado.nextDouble(); lecturaCorrecta = true;
} catch (InputMismatchException e1) {
basura = teclado.nextLine(); // se extrae el token
}
}
if (!lecturaCorrecta) {
System.out.println("Error, " + basura + " no es un número real");
} else { ... }
PRG. ETSINF. UPV - Curso 2018/19 20
Ficheros de texto. Lectura: Scanner
• Ejemplo. Lectura de datos de teclado resistente a errores de pulsación.

Scanner teclado = new Scanner(System.in).useLocale(Locale.US);


double x = 0;
boolean leidoX = false;
do {
System.out.print("Teclee un real (en notacion americana): ");
try {
x = teclado.nextDouble();
leidoX = true;

} catch (InputMismatchException e) {
System.out.println("Dato erroneo. Vuelva a intentarlo.");
String basura = teclado.next();
}
} while (!leidoX);

...

Si se ha producido la excepción y no se extrae el token erróneo, el bucle


intentaría leerlo repetidamente sin éxito.

PRG. ETSINF. UPV - Curso 2018/19 21


Ficheros de texto. Lectura: Scanner
• Con Scanner, además de procesar token a token, se puede sacar una línea:
nextLine() devuelve un String con los caracteres que encuentra hasta el siguiente '\n',
exclusive. Avanza la posición de lectura a continuación del '\n'.
• Ejemplo.
String fuente = "Hola a tots\nHi!\nHello world";
Scanner entrada = new Scanner(fuente);
String palabra = entrada.next(); // palabra es "Hola"
'H' 'o' 'l' 'a' ' ' 'a' ' ' 't' 'o' 't' 's' '\n' ..

String linea = entrada.nextLine(); // linea es " a tots"


... 't' 'o' 't ' 's' '\n' 'H' 'i' '!' '\n' 'H' ...

palabra = entrada.next(); // palabra es "Hi!"


... 't' 'o' 't ' 's' '\n' 'H' 'i' '!' '\n' 'H' ...

linea = entrada.nextLine(); // linea es ""

... 't' 'o' 't ' 's' '\n' 'H' 'i' '!' '\n' 'H' ...
PRG. ETSINF. UPV - Curso 2018/19 22
Ficheros de texto. Lectura: Scanner
• Ejercicio. Considerar la siguiente clase, cuyo main pide al usuario que introduzca una longitud
máxima de línea en el rango [0,80], y a continuación una línea de texto. Se desea escribir en la
salida la comparación de la longitud de la línea con la máxima longitud introducida.
import java.util.*; import java.io.*;
public class LeerLinea {
public static void main(String[] args){

Scanner t = new Scanner(System.in);


boolean lecturaCorrecta = false; int n = 0;
do {
try {
System.out.print("Longitud maxima de las lineas (entre 0 y 80)? ");
n = t.nextInt();
if (0 <= n && n <= 80) { lecturaCorrecta = true; }
else { System.out.println("Longitud fuera del rango [0,80]."); }
} catch (InputMismatchException e1) {
System.out.println("No ha tecleado un entero. ");
String basura = t.nextLine();
}
} while (!lecturaCorrecta);

System.out.print("Escriba una linea de texto: ");


String linea = t.nextLine(); int l = linea.length();
System.out.println("Ha escrito una linea de " + l + " caracteres.");
if (l > n) { System.out.println("Linea demasiado larga."); }
}

}
PRG. ETSINF. UPV - Curso 2018/19 23
Ficheros de texto. Lectura: Scanner
• Si se ejecuta el main de la clase, se observa una salida como la siguiente:

• Corregir el código para que la salida sea la esperada. Solución:


...
boolean lecturaCorrecta = false; int n = 0;
do {
try {
System.out.print("Longitud maxima de las lineas (entre 0 y 80)? ");
n = t.nextInt();
if (0 <= n && n <= 80) { lecturaCorrecta = true; }
else { System.out.println("Longitud fuera del rango [0,80]."); }
} catch (InputMismatchException e) {
System.out.println("No ha tecleado un entero. ");
El finally se ejecuta siempre. Entonces:
} finally { 1. Si no se da la excepción, “limpia” el '\n' del buffer.
t.nextLine(); 2. Si se da la excepción, “limpiaˮ del buffer el token
} erróneo y el '\n'.
} while (!lecturaCorrecta);
...

PRG. ETSINF. UPV - Curso 2018/19 24


Ficheros de texto. Lectura: Scanner
• Para leer de fichero de texto del sistema de archivos, usaremos la constructora
Scanner(File).
• Ejemplo. Programa que escribe los diez primeros enteros positivos en un fichero de texto,
y después los lee de dicho fichero mostrándolos por pantalla.

import java.io.FileNotFoundException;
import java.util.Scanner;
public class TestPrintWriter1 {
public static void main(String[] args) {
String fichero = "file1.txt";
try {
PrintWriter pw = new PrintWriter(new File(fichero));
for (int i = 0; i < 10 ; i++) { pw.println(i); }
pw.close();
Scanner entrada = new Scanner(new File(fichero));
while (entrada.hasNext()) {
System.out.println("Valor leido: "
+ entrada.nextInt());
}
entrada.close();
} catch (FileNotFoundException e) {
System.err.println("Problemas al abrir " + fichero);
}
} Los tokens se van a leer del fichero de texto de nombre file.txt
} PRG. ETSINF. UPV - Curso 2018/19 25
Ficheros de texto. Lectura: Scanner
• ¿ Qué salida produce el siguiente programa, siendo ejemplo.txt el de la figura?
public class TestScanner {
public static void main(String[] args) {
System.out.println("Se leen 3 numeros y una linea de texto");
Scanner entrada = null;
try {
entrada = new Scanner(new File("ejemplo.txt"));
} catch(FileNotFoundException e) {
System.err.println("El fichero no existe." + e.getMessage());
System.exit(0);
}
int n1 = entrada.nextInt();
int n2 = entrada.nextInt();
int n3 = entrada.nextInt();
entrada.nextLine();
String linea = entrada.nextLine();
System.out.println("Los tres numeros son: " + n1 + ", " + n2
+ ", " + n3);
System.out.println("La linea es: " + linea);
entrada.close(); ejemplo.txt
} 1 2
} 3 4 5
Ultima linea.
PRG. ETSINF. UPV - Curso 2018/19 26
Cierre de ficheros
• Los ficheros siempre deben cerrarse explícitamente al acabar de utilizarse.
En particular, un fichero sobre el que se ha escrito, debe ser cerrado antes de poder
ser abierto para lectura (para garantizar la escritura de datos en el disco), y que
pueda ser usado por otra aplicación.

• Si el programador no cierra de forma explícita el fichero, Java lo hace


automáticamente al acabar la ejecución del programa, pero puede quedar corrupto
o incompleto si el programa acaba anormalmente (corte de alimentación, ...).

• Para ello las clases Scanner y PrintWriter tienen el método:


public static void close()

• Cuando se abren y manipulan ficheros en una instrucción try-catch, y no se


puede garantizar que todos los ficheros queden cerrados en cualquier circunstancia,
se usa un bloque finally para cerrarlos:
Un bloque finally se ejecuta siempre, (incluso si la ejecución del try o de un
catch supusiera la finalización del programa).

PRG. ETSINF. UPV - Curso 2018/19 27


Cierre de ficheros
• Ejemplo. Programa que concatena en la salida estándar el contenido de dos ficheros de texto.
import java.util.Scanner; import java.io.FileNotFoundException;
public class Concatena {
public static void main(String args[]) {
String s1 = .... , s2 = ....; // Nombres de los ficheros
File f1 = new File(s1), f2 = new File(s2), f = null;
Scanner ent1, ent2 = null;
try { Si ent1 se crea normalmente,
f = f1; ent1 = new Scanner(f1); esta instrucción es alcanzable.
lista(ent1); ent1.close(); Si ent2 se crea normalmente,
f = f2; ent2 = new Scanner(f2); esta instrucción es alcanzable.

O
lista(ent2); ent2.close();
} catch (FileNotFoundException e) {
System.out.println("Error al abrir " + f);
}
} siempre DEBECERRARSE
/** Metodo auxiliar. Escribe las lineas de s en la salida estandar */
private static lista(Scanner s) {
while (s.hasNext()) {
String linea = s.nextLine();
System.out.printl(linea);
}
}
PRG. ETSINF. UPV - Curso 2018/19 28
Cierre de ficheros
• Ejemplo. Programa que crea una copia de seguridad de un fichero: crea una copia y la deja sólo
de lectura; si la copia no se puede crear, el programa termina sin hacer nada más.

import java.util.Scanner;
import java.io.PrintWriter;
import java.io.FileNotFoundException;
public class CopiaSeguridad {

public static void main(String[] args) {


File fE = new File("fich.txt"), fS = new File("fichBack.txt");

.....

/** Metodo auxiliar. Escribe las lineas de s en p. */


private static void copia(Scanner s, Printwriter p) {
while (s.hasNext()) {
String linea = s.nextLine();
p.println(linea);
}
}
}
PRG. ETSINF. UPV - Curso 2018/19 29
Cierre de ficheros
• Ejemplo. Programa que crea una copia de seguridad de un fichero.
Método main del programa:
public static void main(String[] args) {
File fE = new File("fich.txt"), fS = new File("fichBack.txt");
File f = null;
Scanner ent = null; PrintWriter sal = null;
try { // Apertura y copia de ficheros
f = fE; ent = new Scanner(f);
f = fS; sal = new PrintWriter(f); Si ent se ha creado normalmente, pero la
copia(ent, sal); creación de sal propaga la excepción, esta
// ent.close(); sal.close(); línea no se alcanzaría.
} catch(FileNotFoundException e) { //Captura de posible excepción
System.err.println("Error " + e + " al abrir " + f);
System.exit(-1); // Finalización del programa
} finally { // Se cierran los ficheros que estuvieran abiertos
if (ent != null) { ent.close(); }
if (sal != null) { sal.close(); }
}

//Resto del programa, sólo si no se ha producido ninguna excepción:


fS.setReadOnly();
}
PRG. ETSINF. UPV - Curso 2018/19 30
Ficheros binarios
• En lugar de almacenar datos en ficheros de texto (escribiendo su representación
textual) también es posible guardarla y recuperarla en ficheros de manera
codificada (un int ocupando 4 bytes, un double 8, etc.): ficheros binarios.

• En general se debe seguir una política de almacenamiento que facilite la


recuperación adecuada de la información.

• Por ejemplo: si se salva el contenido de una agenda, almacenando los valores


elementales de cada uno de sus componentes, se puede agrupar la información
en registros: grupos de información semejante que se repiten.

Nombre Dirección Teléfono Nombre Dirección Teléfono …

campo

registro registro

fichero
PRG. ETSINF. UPV - Curso 2018/19 31
Ficheros binarios de acceso secuencial
• En Java también es posible almacenar una secuencia de objetos para poder
recuperarlos posteriormente en el mismo orden. Este proceso se denomina
serialización, y se habla de ficheros binarios de acceso secuencial (fbs).
• Por ejemplo:
– Es posible salvar el contenido de una agenda (objeto de la clase Agenda)
almacenando uno a uno sus objetos constituyentes (objetos de tipo
ItemAgenda).
– O incluso se podría salvar en el fichero un objeto de tipo Agenda (que
incluiría internamente todos los ItemAgenda que lo forman).
• Un fichero puede contener objetos de diferentes clases o incluso mezclar
objetos y valores elementales:

Atributos de un objeto Agenda Atributos de un objeto de otra clase …

objeto Agenda objeto

fichero
PRG. ETSINF. UPV - Curso 2018/19 32
Ficheros binarios de acceso secuencial
• En Java es posible escribir o leer en ficheros binarios secuenciales :
– Valores de tipo primitivos como boolean, int, double, etc.,
– Objectos, con todos sus componentes internos (incluyendo todos los
objetos referenciados por los primeros, y así sucesivamente, de manera
recursiva).
• Los métodos que simplifican esta tarea al programador los
proporcionan unas clases del paquete java.io:
– ObjectInputStream, para lectura, y
– ObjectOutputStream, para escritura.

PRG. ETSINF. UPV - Curso 2018/19 33


Escritura en fbs: la clase ObjectOutputStream
• La clase contiene métodos para escribir tipos primitivos y objetos en un flujo de salida.
Método Descripción
ObjectOutputStream(OutputStream out) Crea un nuevo objeto a partir del flujo de
incluyendo la clase derivada FileOutputStream salida.
void writeInt(int v) Escribe el entero v en 4 bytes.
void writeLong(long v) Escribe el long v al stream de salida como 8
bytes.
void writeUTF(String s) Escribe el String s con formato portable
(UTF8 m.).
void writeDouble(double v) Escribe el double v al flujo de salida como 8
bytes.
void writeObject(Object o) Escribe o en el flujo de salida. Provoca la
escritura del objeto o, y recursivamente de
los subobjetos que lo conforman.

• A diferencia de la clase PrintWriter (escritura de texto), la única constructora de la


clase precisa como parámetro un flujo de salida OutputStream (o de clase derivada): si
queremos escribir en un fichero binario secuencial, primero hay que definir el
FileOutputStream correspondiente.
PRG. ETSINF. UPV - Curso 2018/19 34
Escritura en fbs: la clase ObjectOutputStream

• Ejemplo.

String nomFich = "/tmp/f.dat";


FileOutputStream salida = new FileOutputStream(nomFich);
// El flujo de salida definido tiene como destino
// el fichero /tmp/f.dat
ObjectOutputStream oos = new ObjectOutputStream(salida);
// Las operaciones de escritura sobre oos tendran como
// destino el fichero /tmp/f.dat
oos.writeInt(45);
...

PRG. ETSINF. UPV - Curso 2018/19 35


Escritura en fbs: la clase ObjectOutputStream
• El método writeObject anterior puede escribir objetos de cualquier clase,
siempre que en la cabecera de ésta se haya añadido la cláusula(*)
implements Serializable

como por ejemplo:


public class ItemAgenda implements Serializable { ... }

(ver el ejemplo de las páginas 40 a 42).

• Cuando se escriben objetos de una determinada clase en un fbs mediante un


ObjectOutputStream, Java anota de qué clase son, para que después se
puedan leer correctamente.

(*) Por motivos que exceden el alcance de este curso.

PRG. ETSINF. UPV - Curso 2018/19 36


Lectura en fbs: la clase ObjectInputStream
• Los ficheros binarios creados con ObjectOutputStream sólo pueden ser leídos con
un ObjectInputStream, a causa de las conversiones de formato que se efectúan.
• La clase contiene métodos análogos a los de ObjectOutputStream:

Método Descripción
ObjectInputStream(InputStream in) Crea un nuevo objeto a partir del flujo de
entrada.
incluyendo la clase derivada FileInputStream

int readInt( ) Lee un int como 4 bytes.


long readLong( ) Lee un long como 8 bytes.
String readUTF( ) Lee un String con formato portable (UTF8
m).
double readDouble( ) Lee un double como 8 bytes.
Object readObject( ) Lee un Object completo de acuerdo con la
estructura concreta de los objetos guardados
en el fichero.

• Para leer de un fichero, primero hay que crear un flujo FileInputStream, y luego
crear a partir de él un ObjectInputStream.
PRG. ETSINF. UPV - Curso 2018/19 37
Excepciones en ObjectOutputStream,
ObjectInputStream
• El constructor de FileOutputStream/FileInputStream puede
lanzar la excepción FileNotFoundException:
– Si se da algún problema al acceder al fichero especificado, o
– en el caso de FileInputStream no existe el fichero que se pretende
abrir.

• Los métodos de escritura y de lectura de estas clases pueden lanzar


excepciones de tipo IOException o derivadas:
– Si se dan problemas al escribir el fichero (permisos, hw., ...).
– Si la clase del objeto que se quiere escribir no es serializable.

• El método de lectura readObject() lanza la excepción


ClassNotFoundException:
– Si no se puede determinar la clase del objeto que se está leyendo.

• ¡Atención!: Todas estas excepciones son comprobadas.

PRG. ETSINF. UPV - Curso 2018/19 38


Ejemplo de fbs de valores de tipo primitivo

• Programa que escribe datos en un fichero binario secuencial y


posteriormente lee los mismos datos del fichero y los escribe en
pantalla:

CalificacionesV0.java. Contiene el código de lectura y


escritura, pero sin tratar las excepciones comprobadas (no
compila).
CalificacionesV1.java. Contiene el mismo código
subsanado con el tratamiento de las excepciones.

PRG. ETSINF. UPV - Curso 2018/19 39


Ejemplo de fbs de objetos
• Ejemplo. Proyecto agenda, escritura y lectura de objetos en un fichero para
almacenar y recuperar los datos de una agenda.
• Se usarán las clases:
– ItemAgenda (información individual de un contacto),
– Agenda (información de todos los contactos),
• Clase ItemAgenda:
import java.io.*;
public class ItemAgenda implements Serializable {
private String nom; private String tel; private int postal;
public ItemAgenda(String n, String t, int p) {
this.nom = n; this.tel = t; this.postal = p;
}

public String toString() {


return this.nom +": "+this.tel+" (" + this.postal +")";
}
// otros métodos ...
} // fin de la clase ItemAgenda
PRG. ETSINF. UPV - Curso 2018/19 40
Ejemplo de fbs de objetos
• Clase Agenda:
import java.io.*;
public class Agenda implements Serializable {
public final static int MAX = 8;
private ItemAgenda[] elArray; private int num;
public Agenda() {
this.elArray = new ItemAgenda[MAX]; this.num = 0;
}
public void insertar(ItemAgenda b) {
if (this.num < this.elArray.length) {
this.elArray[this.num] = b; this.num++;
}
}
public String toString() {
String res="";
for (int i = 0;i < num;i++) { res += elArray[i] + "\n"; }
res += "=================================";
return res;
}
...

PRG. ETSINF. UPV - Curso 2018/19 41


Ejemplo de fbs de objetos
• En la clase Agenda se definen además métodos para escribir y leer un objeto
Agenda :

public void guardarAgenda(String fichero)

public static Agenda recuperarAgenda(String fichero)

• La clase PruebaGuardarAgenda crea una Agenda con tres ítems, y la


guarda en el fichero de nombre agenda1.dat.
• La clase PruebaRecuperarAgenda pide al usuario el nombre del fichero
que contiene la agenda e intenta leerla del fichero. Si lo consigue, muestra la
agenda por pantalla.

PRG. ETSINF. UPV - Curso 2018/19 42


Determinación del final del fichero
• A menudo no se conoce a priori el número de elementos que contiene un
fichero.
• En el caso de los InputObjectStream, cuando se leen valores
elementales se produce, al intentar acceder más allá del final del fichero,
la excepción EOFException (subclase de IOException).
• Es posible tratar todos los elementos de un fichero leyéndolos uno a uno
repetidamente y capturando la excepción que se produzca cuando se
llegue a final de fichero.

• El ejemplo siguiente muestra el uso de esta técnica.

PRG. ETSINF. UPV - Curso 2018/19 43


Determinación del final del fichero
• Ejemplo. Lectura de un fichero binario secuencial de int, para el que no se conoce el
número de valores que contiene.
• Los valores leídos se escriben uno detrás de otro y se escribe un aviso al llegar al final
de fichero.
public static void leerFicheroInt(String nomFichero) {
try {
FileInputStream f = FileInputStream(nomFichero);
ObjectInputStream ois = new ObjectInputStream(f);
try {
while (true) {
int val = ois.readInt();
System.out.println(val);
}
} catch (EOFException ef) {
System.out.println("Se ha llegado al final del fichero");
}
ois.close();
} catch (IOException fex) {
System.err.println("Error al recuperar dato: "
+ fex.getMessage());
}
} PRG. ETSINF. UPV - Curso 2018/19 44
Resumen
• El uso de ficheros permite que la información sobreviva a la ejecución de los
programas mediante el uso de almacenamiento en memoria secundaria.

• Java permite la creación de ficheros binarios independientes de la


plataforma y de texto usando mecanismos basados en flujos (streams):
secuencia de información + origen/destino

• La forma más cómoda para interactuar con ficheros de texto:


– PrintWriter (escritura),
– Scanner (lectura).

• La forma más cómoda para interactuar con ficheros binarios secuenciales de


valores elementales y/o objetos:
– ObjectOutputStream (escritura),
– ObjectInputStream (lectura)

PRG. ETSINF. UPV - Curso 2018/19 45


Recomendaciones al trabajar con ficheros
• Es importante gestionar las excepciones en los procesos de E/S:
– Incoherencias de tipos (InputMismatchException) al leer de un
Scanner .
Es una clase de excepción no comprobada, luego el programador la
puede tratar o no según lo que convenga en cada caso.
– Errores generales de E/S (IOException), y sus casos particulares:
Ficheros inexistentes o con problemas (FileNotFoundException),
condición de final de fichero (EOFException).
Son clases de excepciones comprobadas, el compilador impide que el
programador se olvide de ellas o las ignore: para que el programa
compile correctamente, debe capturarlas o propagarlas explícitamente.

• Los flujos siempre deben cerrarse explícitamente al acabar de utilizarse.


Todas las clases presentadas tienen un método close() para cerrar el
flujo.
PRG. ETSINF. UPV - Curso 2018/19 46

S-ar putea să vă placă și