Escolar Documentos
Profissional Documentos
Cultura Documentos
1. INTRODUCCIÓN....................................................................................................... 3
1.1. TIPOS DE FICHEROS ............................................................................................. 3
EJERCICIO 1.................................................................................................... 5
EJERCICIO 2.................................................................................................... 8
EJERCICIO 3.................................................................................................. 13
EJERCICIO 4.................................................................................................. 19
EJERCICIO 5.................................................................................................. 25
1. INTRODUCCIÓN
En las aplicaciones que hemos realizado hasta el momento, los datos que se han
manejado tenían un periodo de vida muy corto, limitado como máximo a la duración del tiempo
de ejecución de la aplicación.
Pero en la mayoría de las aplicaciones reales se trabaja con datos existentes en algún
dispositivo de almacenamiento permanente, como un fichero o una base de datos. Estos
programas, deberán tener capacidad tanto para recuperar datos de estos dispositivos, como
para almacenar información en los mismos.
En este tema vamos a tratar el acceso a ficheros, dejando para temas posteriores el
tratamiento de bases de datos, pues las clases que proporciona Java para trabajar con uno y
otro tipo de dispositivo de almacenamiento son diferentes.
El paquete java.io del Java estándar incluye todas las clases necesarias para acceder a
ficheros de disco desde una aplicación Java. Algunas de estas clases son sólo utilizadas con
ficheros, sino para realizar operaciones de entrada/salida de datos con diferentes tipos de
dispositivos externos. Este es el caso de BufferedReader que, aunque ha sido utilizada hasta el
momento para recuperar datos del teclado, puede ser empleada también para recuperar datos
de un fichero de texto.
Algunas de las clases más importantes incluidas en este paquete para el tratamiento de
ficheros son:
BufferedReader (Reader r)
Donde el objeto Reader tendrá que ser un lector de entrada asociado al fichero des que
se quiere realizar la lectura. Este lector de entrada podrá ser un objeto FileReader, cuyo
constructor:
nos permite crear un objeto de este tipo a partir de la localización del fichero.
Hay que tener en cuenta también que la creación del objeto FileReader puede provocar
una excepción de tipo FileNotFoundException que habrá que capturar. Esta excepción se
produce si el fichero especificado en la ruta no existe.
Una vez que se dispone del objeto BufferedReader se puede invocar a su método
readLine() para recuperar su contenido línea a línea. Cada llamada a readLine() devuelve la
línea actual y sitúa el puntero de lectura en la siguiente línea del fichero. Después de leer la
última línea el puntero se situará al final del fichero; una vez en esa posición, la llamada al
método readLine() devolverá null.
Una vez que se han terminado de realizar las operaciones de lectura sobre el fichero,
conviene cerrar el flujo de entrada para que se liberen los recursos utilizados. Esta operación
se lleva a cabo mediante el método close() de la clase FileReader:
reader.close();
EJERCICIO 1
4.5|3|3.75|
8|6.8
podemos crear un objeto PrintWriter asociado al fichero cuya dirección se suministra como
argumento. Si el fichero no existiera, se crearía al crear el objeto.
Una vez creado el objeto PrintWriter se puede recurrir a sus métodos de escritura para
enviar información al fichero. Entre estos métodos destacan:
print (tipo dato). El juego de métodos print nos permite enviar cualquier tipo
de dato primitivo Java y String al fichero. Este método se encuentra
sobrecargado, existiendo un método print por cada tipo primitivo (a excepción
de byte y short) además de String. Tras escribir el dato, el puntero de escritura
se situaría en la siguiente posición libre dentro del fichero.
printf (String formato, Object ... datos). Este método también lo estudiamos
en los primeros temas de Java. A través de él podemos escribir un texto
formateado en el fichero.
Al igual que sucede al realizar las operaciones de lectura del fichero, tras escribir los
datos en el fichero, el flujo de salida debería cerrarse para liberar los recursos utilizados. El
método close() de PrintWriter lleva a cabo esta tarea.
Una vez creado el objeto FileWriter en el modo deseado, las operaciones de escritura
se realizarán a través de su método write().
EJERCICIO 2
Figura. 1.
package graficos;
import javax.swing.*;
public class VentanaSwing extends JFrame{
JTextField jnota;
public VentanaSwing(String title, int x, int y, int w,
int h){
super(title);
this.getContentPane().setLayout(null);
this.setBounds(x, y, w, h);
JLabel jl = new JLabel ("Introduce la nota: ");
jl.setBounds(50, 100, 140, 40);
jnota = new JTextField ();
jnota.setBounds(200, 100, 100, 40);
JButton jb=new JButton("Añadir");
jb.setBounds(150, 200, 120, 40);
//se añaden los controles al panel de contenido
this.getContentPane().add(jl);
this.getContentPane().add(jnota);
this.getContentPane().add(jb);
//asocia el botón con su manejador
jb.addActionListener(new GestionFichero(this));
this.setVisible(true);
}
}
Como ya sabemos, cada nota tendrá que ser separada de la siguiente por el carácter
“|”, además, tras la escritura de cinco notas seguidas se introducirá un salto de línea. Para
introducir dicho salto de línea, haremos uso de la propiedad del sistema line.separator en vez
de indicar directamente “\n”, ya que esta cadena puede no representa el carácter de salto de
línea en todos los sistemas.
Con todo ello, el código de la clase manejadora del botón “Añadir” quedaría:
package graficos;
import java.awt.event.*;
import java.io.*;
public class GestionFichero implements ActionListener{
private VentanaSwing v;
private int contador;
public GestionFichero(VentanaSwing v){
this.v=v;
contador=0;
}
public void actionPerformed(ActionEvent e){
try{
FileWriter writer = new FileWriter("notas.txt",true);
writer.write(v.jnota.getText()+"|");
contador++;
//se incluye un salto de línea
//tras escribir cinco notas
if(contador==5){
writer.write(System.getProperty ("line.separator"));
contador=0;
}
writer.close();
}
catch(FileNotFoundException ex){
System.out.println("El fichero indicado no existe");
}
catch(IOException ex){
System.out.println("No se permiten más operaciones
sobre el fichero");
}
}
}
Para finalizar con la clase FileWriter, comentar que es posible utilizar el siguiente
constructor de la clase PrintWriter para crear un objeto de este tipo a partir de un objeto
FileWriter:
De esta manera, se podrían utilizar los métodos de PrintWriter para escribir sobre un
fichero de texto permitiendo que los nuevos datos se añadieran al final del mismo.
Si vamos a trabajar con tipos primitivos, por ejemplo números, puede ser resultar más
cómodo almacenar la información en un fichero binario en vez de en un fichero de texto. Al
utilizar ficheros binarios, la información puede recuperarse directamente en su tipo original,
evitando de este modo el empleo de métodos de conversión.
Como en el caso de los ficheros de texto, el objeto de escritura debe cerrarse una vez
finalizadas las operaciones para liberar los recursos.
3.2. RECUPERACIÓN DE DATOS
En caso de que el fichero no exista, la creación del objeto FileInputStream lanzará una
excepción FileNotFoundException.
Cuando se hace la llamada a uno de los métodos de lectura, se recupera el dato que
se encuentra en la posición actual del puntero, pasando después a apuntar al siguiente dato.
Una vez leído el último dato, el puntero se situará en la posición EOF (final de fichero); a partir
de ahí, cualquier nueva llamada a los métodos de lectura provocará una excepción de tipo
EOFException, que es una subclase de IOException. Así pues, si quisiéramos recuperar todos
los datos contenidos en el fichero, deberíamos incluir las instrucciones de lectura y
manipulación de los datos en un bucle infinito, del cual se saldría en el momento en que se
produjera la excepción EOFException.
try{
FileInputStream fi = new FileInputStream(
"c:\\contenido.dat");
DataInputStream entrada=new DataInputStream(fi);
while(true){
System.out.println(entrada.readInt());
}
}
catch(FileNotFoundException e){
System.out.println("El fichero indicado no existe");
}
catch(EOFException e){
System.out.println("Se han leído todos los datos");
}
catch(IOException e){
System.out.println("No se permiten más operaciones
sobre el fichero");
}
EJERCICIO 3
Por otro lado, realizaremos un modificación el interfaz gráfica para que además de la
introducción de los datos, disponga de un botón que al ser pulsado nos muestre la nota media
en un JLabel de la propia interfaz (figura 2).
Figura. 2.
package graficos;
import javax.swing.*;
public class VentanaSwing extends JFrame{
JTextField jnota;
JLabel jlmedia;
public VentanaSwing(String title, int x, int y, int w,
int h){
super(title);
this.getContentPane().setLayout(null);
this.setBounds(x, y, w, h);
JLabel jl = new JLabel ("Introduce la nota: ");
jl.setBounds(50, 100, 140, 40);
jnota = new JTextField ();
jnota.setBounds(200, 100, 100, 40);
JButton jb=new JButton("Añadir");
jb.setBounds(150, 200, 120, 40);
JButton jbmedia=new JButton("Mostrar media");
jbmedia.setBounds(100, 300, 150, 40);
jlmedia=new JLabel("");
jlmedia.setBounds(300, 300, 120, 30);
//se añaden los controles al panel de contenido
this.getContentPane().add(jl);
this.getContentPane().add(jnota);
this.getContentPane().add(jb);
this.getContentPane().add(jbmedia);
this.getContentPane().add(jlmedia);
//asocia el botón Añadir con su manejador
jb.addActionListener(new GestionFichero(this));
//asocia el botón Media con su manejador
jbmedia.addActionListener(new GestionMedia(this));
this.setVisible(true);
}
}
En cuanto a la clase GestionFichero, el nuevo código será el que se muestra en el
siguiente listado:
import java.awt.event.*;
import java.io.*;
public class GestionFichero implements ActionListener{
private VentanaSwing v;
public GestionFichero(VentanaSwing v){
this.v=v;
}
public void actionPerformed(ActionEvent e){
try{
FileOutputStream salida = new
FileOutputStream("notas.bin",true);
DataOutputStream fichero=new DataOutputStream(salida);
fichero.writeInt(Integer.parseInt(v.jnota.getText()));
salida.close();
}
catch(FileNotFoundException ex){
System.out.println("El fichero indicado no existe");
}
catch(IOException ex){
System.out.println("No se permiten más operaciones
sobre el fichero");
}
}
}
Finalmente, la clase que gestiona la pulsación del botón “Mostrar media” quedará como
se indica en el siguiente listado:
package graficos;
import java.awt.event.*;
import java.io.*;
public class GestionMedia implements ActionListener{
private VentanaSwing v;
public GestionMedia(VentanaSwing v){
this.v=v;
}
public void actionPerformed(ActionEvent e){
double media=0.0;
int contador=0;
FileInputStream entrada=null;
try{
entrada = new FileInputStream("notas.bin");
DataInputStream datos=new DataInputStream(entrada);
//recupera todos los datos del fichero mientras
//no llegue al final del mismo
while(true){
media+=datos.readInt();
contador++;
}
}
catch(FileNotFoundException ex){
System.out.println("El fichero especificado no
existe");
}
catch(EOFException ex){
media=media/contador;
v.jlmedia.setText(String.valueOf(media));
}
catch(IOException ex){
System.out.println("No se permiten más operaciones
sobre el fichero");
}
finally{
try{
entrada.close();
}
catch(IOException ex){}
}
}
}
La forma de gestionar objetos en disco es muy similar a la seguida con los tipos
primitivos; en vez de usar las clases “Data”, se emplearán ObjectOutputStream y
ObjectInputStream, apoyándose en FileOutputStream y FileInputStream.
Para que un objeto pueda ser serializado, la clase a la que pertenece el objeto (o su
superclase) debe implementar la interfaz java.io.Serializable. Esta interfaz no dispone de
ningún método que tenga que implementar el programador; simplemente con indicar en la
definición de la clase que se implementa la interfaz:
será suficiente para que la JVM se encargue de realizar las operaciones oportunas para
serializar y desserializar el objeto.
Muchas de las clases del Java SE de uso general, como String, las clases de envoltorio
o ArrayList, implementan Serializable. En el caso de clases personalizadas será necesario
especificar la implementación de la interfaz si queremos que sus objetos puedan ser
almacenados en disco. El siguiente listado corresponde a un ejemplo de clase JavaBean que
implementa Serializable:
Este flujo de salida será, como en el caso de los datos primitivos Java, un objeto
FileOutputStream. Las siguientes instrucciones crearían un ObjectOutputStream para escribir
objetos en el fichero “contenido.dat” en modo append:
Cada llamada a readObject() devuelve el objeto que actualmente está siendo apuntado
por el puntero de lectura, desplazando después dicho puntero a la siguiente posición. En este
sentido, hay que tener en cuenta que después de leer el último objeto del fichero la
siguiente llamada al método readObject() provocará una excepción IOException.
EJERCICIO 4
En este ejercicio realizaremos una aplicación basada en entorno gráfico que permita
grabar en disco objetos de tipo Empleado. También dispondrá de una opción que permita
mostrar el nombre de un empleado a partir de su código. El aspecto de la interfaz será el que
se indica en la figura 4.
package graficos;
import javax.swing.*;
public class Interfaz extends JFrame{
JTextField jtcodigo,jtnombre,jtinput;
JLabel jlout;
public Interfaz(String title, int x, int y, int w, int h){
super(title);
this.getContentPane().setLayout(null);
this.setBounds(x, y, w, h);
JLabel jlcodigo = new JLabel ("Código Empleado: ");
jlcodigo.setBounds(50, 50, 140, 30);
jtcodigo = new JTextField ();
jtcodigo.setBounds(200, 50, 100, 30);
JLabel jlnombre = new JLabel ("Nombre Empleado: ");
jlnombre.setBounds(50, 100, 140, 30);
jtnombre = new JTextField ();
jtnombre.setBounds(200, 100, 100, 30);
JButton jb=new JButton("Añadir");
jb.setBounds(150, 150, 120, 30);
JLabel jlinput = new JLabel ("Introduce Codigo: ");
jlinput.setBounds(50, 250, 140, 30);
jtinput = new JTextField ();
jtinput.setBounds(200, 250, 100, 30);
JButton jbbuscar=new JButton("Buscar");
jbbuscar.setBounds(100, 300, 150, 40);
jlout=new JLabel("");
jlout.setBounds(300, 300, 120, 30);
//se añaden los controles al panel de contenido
this.getContentPane().add(jlcodigo);
this.getContentPane().add(jlnombre);
this.getContentPane().add(jlout);
this.getContentPane().add(jtcodigo);
this.getContentPane().add(jtnombre);
this.getContentPane().add(jtinput);
this.getContentPane().add(jlinput);
this.getContentPane().add(jb);
this.getContentPane().add(jbbuscar);
package graficos;
import java.io.*;
public class Empleado implements Serializable{
private int codigo;
private String nombre;
public Empleado(){}
public Empleado(int codigo, String nombre){
this.codigo=codigo;
this.nombre=nombre;
}
public int getCodigo() {
return codigo;
}
public void setCodigo(int codigo) {
this.codigo = codigo;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
}
La clase GestionGuardar se encarga de gestionar el almacenamiento de los empleados
en el fichero. Como vemos en el siguiente listado, el fichero se prepara para la escritura en el
constructor de la clase, realizándose las llamadas a writeObject() con un nuevo objeto cada vez
que se pulsa el botón “Añadir”:
package graficos;
import java.awt.event.*;
import java.io.*;
public class GestionGuardar implements ActionListener{
private Interfaz v;
private FileOutputStream salida;
private ObjectOutputStream fichero;
public GestionGuardar(Interfaz v){
this.v=v;
try{
salida = new FileOutputStream("empleados.bin");
fichero=new ObjectOutputStream(salida);
}
catch(FileNotFoundException ex){
System.out.println("El fichero indicado no existe");
}
catch(IOException ex){
ex.printStackTrace();
}
}
public void actionPerformed(ActionEvent e){
try{
//crea el objeto Empleado con los datos de
//los campos de texto
Empleado em=new Empleado(Integer.parseInt(
v.jtcodigo.getText()),v.jtnombre.getText());
fichero.writeObject(em);
}
catch(IOException ex){
System.out.println("No se permiten más operaciones
sobre el fichero");
}
}
}
En cuanto a la clase que gestiona la búsqueda del Empleado, su código será como se
indica a continuación:
package graficos;
import java.awt.event.*;
import java.io.*;
public class GestionBusqueda implements ActionListener{
private Interfaz v;
boolean encontrado=false;
public GestionBusqueda(Interfaz v){
this.v=v;
}
public void actionPerformed(ActionEvent e){
encontrado=false;
FileInputStream entrada=null;
try{
entrada = new FileInputStream("empleados.bin");
ObjectInputStream datos=
new ObjectInputStream(entrada);
while(true){
Empleado emp=(Empleado)datos.readObject();
//si el código de empleado existe se
//muestra el nombre del empleado
if(emp.getCodigo()==
Integer.parseInt(v.jtinput.getText())){
v.jlout.setText(emp.getNombre());
encontrado=true;
}
}
}
catch(ClassNotFoundException ex){
ex.printStackTrace();
}
catch(FileNotFoundException ex){
System.out.println("El fichero especificado no
existe");
}
catch(IOException ex){
//si despues de mrecorrer todos los empleados
//no se encuentra el código se muestra el mensaje
if(!encontrado){
v.jlout.setText("No existe ningún empleado con
ese nombre");
}
ex.printStackTrace();
}
finally{
try{
entrada.close();
}
catch(IOException ex){}
}
}
}
Donde el segundo parámetro “modo”, es una cadena de caracteres que nos indica el
modo de acceso al fichero. Entre sus posibles valores destacamos:
“rw”. Acceso en modo lectura y escritura. Cuando se crea el objeto con este
modo de acceso se podrán realizar tanto operaciones de lectura de datos
como de escritura, y si el fichero no existe se creará.
A continuación, vamos a ver los principales métodos que nos proporciona esta clase y
que nos permitirán realizar operaciones de lectura y escritura de datos en cualquier parte del
mismo. Destacamos los siguientes:
writeXxx (tipo dato). El juego de métodos writeXxx() escriben un dato del tipo
especificado en la posición actual del fichero, siendo Xxx y tipo el nombre del
tipo primitivo. Por ejemplo, writeInt (int dato) se utilizaría para escribir datos de
tipo int, mientras que writeBoolean (boolean dato) almacenaría un dato
boolean. Después de realizar la escritura del dato, el puntero se desplaza
tantos bytes como ocupe el dato escrito.
EJERCICIO 5
import java.io.*;
import java.util.*;
public class Test {
public static void main(String[] args) {
try{
RandomAccessFile rfile=
new RandomAccessFile("numeros.bin","rw");
for(int i=1;i<=10;i++){
int num=(int)(Math.random()*100);
System.out.println("Número "+i+" :"+num);
rfile.writeInt(num);
}
//cierra y abre de nuevo para sólo lectura
rfile.close();
rfile=new RandomAccessFile("numeros.bin","r");
//solicita la posición del número a recuperar
Scanner sc = new Scanner(System.in);
System.out.println("Introduzca la posición del
número que quiere recuperar:");
int num=Integer.parseInt(sc.nextLine());
//lee el número que ocupa la posición indicada,
//teniendo en cuenta que cada int ocupa 4 bytes
int pos=(num-1)*4;
//si existe la posición indicada
if(pos<(rfile.length()-4)){
rfile.seek(pos);
System.out.println(rfile.readInt());
}
else{
System.out.println("No se puede leer más allá de
la última posición");
}
rfile.close();
}
catch (FileNotFoundException e){
System.out.println("Fichero no existente");
}
catch(IOException e){
e.printStackTrace();
}
}
}
AUTOEVALUACIÓN
1. Indica cual de las siguientes instrucciones permitiría crear un objeto para realizar
operaciones de lectura de cadenas de caracteres sobre el fichero contenido.txt:
B. BufferedReader reader =
new BufferedReader (new FileReader( “contenido.txt”));
C. BufferedReader reader = new BufferedReader (“contenido.txt”);
D. Se creará el fichero
3. Para escribir cadenas de texto en un fichero, de modo que los nuevos datos se
añadan al final del mismo manteniendo el contenido anterior, la creación del objeto
FileWriter debería realizarse:
int suma=0;
try{
suma+=entrada.readInt();
}
catch(FileNotFoundException e){
catch(EOFException e){
catch(IOException e){
A. while(true){
C. while(entrada.readLine()!=null){
D. while(entrada.readInt()!=0){