Manejo de Archivos en JAVA
Manejo de Archivos en JAVA
Manejo de Archivos en JAVA
La forma de interactuar con los sistemas de archivos locales se realiza a travs de la clase File, esta clase proporciona muchas utilidades relacionadas con archivos y con la obtencin de informacin bsica sobre esos archivos.
Comprobaciones y Utilidades
Una vez creado un objeto File, se puede utilizar uno de los siguientes mtodos para reunir informacin sobre el archivo:
Nombres de archivo: String getName() String getPath() String getAbsolutePath() String getParent() boolean renameTo( File nuevoNombre ) Comprobaciones: boolean exists() boolean canWrite() boolean canRead() boolean isFile() boolean isDirectory() boolean isAbsolute() Informacin general del archivo: long lastModified() long length() Utilidades de directorio: boolean mkdir() String[] list()
Hay muchas clases dedicadas a la obtencin de entrada desde un archivo. Este es el esquema de la jerarqua de clases de entrada por archivo:
Objetos FileInputStream
Los objetos FileInputStream tpicamente representan archivos de texto accedidos en orden secuencial, byte a byte. Con FileInputStream, se puede elegir acceder a un byte, varios bytes o al archivo completo.
Apertura de un FileInputStream
Para abrir un FileInputStream sobre un archivo, se le da al constructor un String o un objeto File:
FileInputStream mi ArchivoSt; miArchivoSt = new FileInputStream( "/etc/kk" );
Lectura de un FileInputStream
Una vez abierto el FileInputStream, se puede leer de l. El mtodo read() tiene muchas opciones:
int read(); //Lee un byte y devuelve -1 al final del stream. int read( byte b[] ); //Llena todo el array, si es posible. Devuelve el nmero de bytes ledos o -1 si se alcanz el final del stream. int read( byte b[],int offset,int longitud );//Lee longitud bytes en b comenzando por b[offset]. Devuelve el nmero de bytes ledos o -1 si se alcanz el final del stream.
Cierre de FileInputStream
Cuando se termina con un archivo, existen dos opciones para cerrarlo: explcitamente, o implcitamente cuando se recicla el objeto (el garbage collector se encarga de ello). Para cerrarlo explcitamente, se utiliza el mtodo close(): miArchivoSt.close();
Objetos DataInputStream
Los objetos DataInputStream se comportan como los FileInputStream. Los streams de datos pueden leer cualquiera de las variables de tipo nativo, como floats, ints o chars. Generalmente se utilizan DataInputStream con archivos binarios.
// Cierra el archivo de datos explcitamente //Siempre se cierra primero el archivo stream de mayor nivel miDStream.close(); miFStream.close();
Lectura de un DataInputStream
Al acceder a un archivo como DataInputStream, se pueden utilizar los mismos mtodos read() de los objetos FileInputStream. No obstante, tambin se tiene acceso a otros mtodos diseados para leer cada uno de los tipos de datos:
byte readByte(); int readUnsignedByte(); short readShort(); int readUnsignedShort(); char readChar(); int readInt(); long readLong(); readFloat(); double readDouble(); String readLine() float
Cada mtodo leer un objeto del tipo pedido. Para el mtodo String readLine(), se marca el final de la cadena con \n, \r, \r\n o con EOF. Para leer un long, por ejemplo:
long numeroSerie; ... numeroSerie = miDStream.readLong();
Ahora se puede utilizar is para leer informacin de la misma forma que se hace con un objeto FileInputStream:
is.read( buffer,0,buffer.length ); NOTA: Debe tenerse muy en cuenta que algunos usuarios pueden haber configurado la seguridad de sus navegadores para que los applets no accedan a archivos.
La contrapartida necesaria de la lectura de datos es la escritura de datos. Como con los Streams de entrada, las clases de salida estn ordenadas jerrquicamente:
Objetos FileOutputStream
Los objetos FileOutputStream son tiles para la escritura de archivos de texto. Como con los archivos de entrada, primero se necesita abrir el archivo para luego escribir en l.
Apertura de un FileOutputStream
Para abrir un objeto FileOutputStream, se tienen las mismas posibilidades que para abrir un archivo stream de entrada. Se le da al constructor un String o un objeto File.
FileOutputStream miArchivoSt; miArchivoSt = new FileOutputStream( "/etc/kk" );
Escritura en un FileOutputStream
Una vez abierto el archivo, se pueden escribir bytes de datos utilizando el mtodo write(). Como con el mtodo read() de los streams de entrada, tenemos tres posibilidades:
void write( int b );//Escribe un byte. void write( byte b[] );//Escribe todo el array, si es posible. void write( byte b[],int offset,int longitud );//Escribe longitud bytes en b comenzando por b[offset].
Cierre de FileOutputStream
Cerrar un stream de salida es similar a cerrar streams de entrada. Se puede utilizar el mtodo explcito: miArchivoSt.close(); O, se puede dejar que el sistema cierre el archivo cuando se recicle miArchivoSt.
Si se trabaja con gran cantidad de datos, o se escriben muchos elementos pequeos, ser una buena idea utilizar un stream de salida con buffer. Los streams con buffer ofrecen los mismos mtodos de la clase FileOutputStream, pero toda salida se almacena en un buffer. Cuando se llena el buffer, se enva a disco con una nica operacin de escritura; o, en caso necesario, se puede enviar el buffer a disco en cualquier momento.
Streams DataOutput
Java tambin implementa una clase de salida complementaria a la clase DataInputStream. Con la clase DataOutputStream, se pueden escribir datos binarios en un archivo.
void writeBoolean( boolean b ); void writeByte( int i ); void writeShort( int i ); void writeChar( int i ); void writeInt( int i ); void writeFloat( float f ); void writeDouble( double d ); void writeBytes( String s ); void writeChars( string s );
Para las cadenas, se tienen dos posibilidades: bytes y caracteres. Hay que recordar que los bytes son objetos de 8 bits y los caracteres lo son de 16 bits. Si nuestras cadenas utilizan caracteres Unicode, debemos escribirlas con writeChars().
Contabilidad de la salida
Otra funcin necesaria durante la salida es el mtodo size(). Este mtodo simplemente devuelve el nmero total de bytes escritos en el archivo. Se puede utilizar size() para ajustar el tamao de un archivo a mltiplo de cuatro. Por ejemplo, de la forma siguiente:
) int numBytes = miDataStream.size() % 4; miDataStream.write( 0 ); for( int i=0; i < numBytes; i++
El argumento modo determina si se tiene acceso de slo lectura (r) o de lectura/escritura (r/w). Por ejemplo, se puede abrir un archivo de una base de datos para actualizacin:
RandomAccessFile miRAFile; miRAFile = new RandomAccessFile( "/tmp/kk.dbf","rw" );
Acceso a la Informacin
Los objetos RandomAccessFile esperan informacin de lectura/escritura de la misma manera que los objetos DataInput/DataOutput. Se tiene acceso a todas las operaciones read() y write() de las clases DataInputStream y DataOutputStream. Tambin se tienen muchos mtodos para moverse dentro de un archivo: long getFilePointer(); Devuelve la posicin actual del puntero del archivo: void seek( long pos );
Coloca el puntero del archivo en una posicin determinada. La posicin se da como un desplazamiento en bytes desde el comienzo del archivo. La posicin 0 marca el comienzo de ese archivo: long length();
Devuelve la longitud del archivo. La posicin length() marca el final de ese archivo.
El paquete java.io provee una librera extensa de clases para trabajar con entrada y salida. Java provee flujos (streams) como un mecanismo general para trabajar con datos I/O. Los flujos implementan acceso secuencial de datos. Hay dos tipos de flujos: flujos de bytes y flujos de caracteres. Un flujo de entrada es un objeto que una aplicacin puede isar para leer una secuencia de datos. Un flujo de salida es un objeto que una aplicacin puede usar para escribir una secuencia de datos. Un fllujo de entrada actua como una fuente de datos. Un flujo de salida actua como un destino de datos. Las siguientes entidades pueden actuar como flujos de entrada y flujos de salida: o Un arreglo de bytes de caracteres. o Un archivo. o Una tubera. o Una conexin de red. Los flujos pueden ser encadenados con filtros para proveer nueva funcionalidad. Adems de trabajar con bytes y caracteres, los flujos proveen entrada y salida de objetos y valores primitivos de Java. EL paquete java.io tambin provee soporte para acceso aleatorio de archivos, y una interfaz genrica para interactuar con el sistema de archivos de la plataforma.
En Java 1.0 la librera I/O fue totalmente orientada a bytes. En versiones posteriores se complement con una librera orientada a caracteres, clases basadas en Unicode.
La clase File
Esta clase no representa referencias a un archivo. Representa el nombre de un archivo particular o los nombres de un conjunto de archivos en un directorio.
Las clases de I/O se dividen en entrada y salida. De InputStream y de Reader se derivan clases con mtodos bsicos read(), y de OutputStream y Writer se derivan clases con mtodos write(). Tales mtodos tienen la capacidad de manipular un byte o arreglo de baytes.
Tipos de InputStream Las diferentes fuentes que producen entradas son: 1. Un arreglo de bytes.
2. 3. 4. 5. 6.
Un objeto String. Un archivo. Una tubera. Una secuencia de flujos. Otras fuentes, como una conexin a Internet.
Tipos de OutputStream Esta categora incluye clases para poner salidas en: arreglo de bytes, archivo o tubera.
Readers y Writers
Las clases Reader y Writer no sustituyen a InputStrean y OutputStream, por el contrario, complementan la librera I/O con funcionalidad para caracteres y en cumplimiento a Unicode. Tambin se incorporan estas clases con el propsito de la Internacionalizacin. La jerarqua de flujos soporta 8 bits, mientras que Unicode maneja 16 bits, por lo tanto las jerarquas de Reader y Writer agregan soporte completo de Unicode a operaciones I/O.
Serializacin de objetos
La serializacin de objetos permite tomar cualquier objeto que implemente la interfaz Serializable y convertirlo en una secuencia de bytes que pueden despus reestablecerse para generar el objeto original. La serializacin permite compensar las diferencias entre sistemas operativos: se puede crear un objeto en Windows, serializarlo y enviarlo a travs de la red a una mquina Unix donde ser correctamente reconstruido. Por lo tanto no es necesario preocuparse por la representacin de datos sobre diferentes mquinas, la ordenacin de bytes o cualquier otro detalle.
La serializacin permite implementar una persistencia ligera. Con la persistencia, la vida de un objeto no se determina por la ejecucin de un programa (el objeto vive entre las invocaciones de un programa). Se dice persistencia ligera porque se hace explcitamente mediante alguna palabra reservada y el sistema se encarga de los detalles. La serializacin sirve para dos propsitos principales:
1. RMI, la cual permite que objetos de otras mquinas se comporten como si estuvieran en la mquina localmente. Aqu la serializacin permite transportar argumentos y variables de retorno al momento de enviar mensajes. 2. JavaBeans, cuando se usan, su estado de informacin normalmente se configura en tiempo de diseo. Tal informacin de estado debe almacenarse y posteriormente recuperarse cuando el programa es iniciado (esta lo hace la serializacin). Para serializar un objeto:
Crear un objeto OutputStream. Envolverlo dentro un objeto ObjectOutputStream. Llamar al mtodo writeObject().
Crear un InputStream. Envolverlo dentro de un ObjectInputStream. Llamar al mtodo readObject(). Se efecta un upcast a Object, por lo que hay que realizar un downcating.
El proceso de serializacin hace que las referencias contenidas en un objeto guarden a esos objetos, de manera recursiva y as sucesivamente. Para tener un control ms preciso sobre el proceso de serializacin se debe utilizar la interfaz Externalizable. Con Externalizable se puede controlar lo que se requiere o no serializar, pero la informacin es sensible y corre riego, por lo tanto hay que tener cuidado sobre su utilizacin.
La diferencia principal entre Serializable y Externalizable al recuperar objetos es que con la primera interfaz el objeto se construye completamente desde sus bits almacenados, mientras que con la segunda el proceso de construccin de objetos se lleva a cabo (incluyendo las iniciaciones hasta el punto de definicin de campo), por lo que la invocacin de los constructores por default se invocan. Utilizando Serializable es posible evitar que algunos campos se serialicen. Esto se hace con la palabra transient
codigo ejemplo:
En el siguiente codigo tenemos lo siguiente: Un mtodo en Java cuya funcin es escribir un nuevo archivo 1 simple linea de texto. EL mtodo consta de un parametro que es el nombre del archivo, por ejemplo "archivo.txt". Debe estar incluida la extensin en el nombre, pues no se asigna por defecto. Veamos el cdigo:
import java.io.*;//no olviden importar esta librera al inicio de su programa //esto es solo un mtodo, no una clase, el mtodo deber ser incluido en una clase java para su uso public void escribir(String nombreArchivo) { File f; f = new File("nombreArchivo"); //Escritura try{ FileWriter w = new FileWriter(f); BufferedWriter bw = new BufferedWriter(w); PrintWriter wr = new PrintWriter(bw); wr.write("Esta es una linea de codigo");//escribimos en el archivo wr.append(" - y aqui continua"); //concatenamos en el archivo sin borrar lo existente //ahora cerramos los flujos de canales de datos, al cerrarlos el archivo quedar guardado con informacin escrita //de no hacerlo no se escribir nada en el archivo
Como se puede apreciar, es necesario incluir el cdigo dentro de un "try" y un "catch" para evitar errores. Ser necesario el uso de 4 clases especiales para poder escribir, la clase File, FileWriter, BufferedWriter y PrintWriter, cada una hace lo siguiente:
1. File: esta clase es la escencia de crear un nuevo archivo, si un archivo con el mismo nombre ya existe podramos sin querer escribir contenido sobre el mismo. 2. FileWriter: es un objeto que tiene como funcin escribir datos en un archivo. 3. BufferedWriter: objeto que reserva un espacio en memoria donde se guarda la informacin antes de ser escrita en un archivo. 4. PrintWriter: Es el objeto que utilizamos para escribir directamente sobre el archivo de texto.
5. import java.io.*; 6. 7. public class LeerArchivo { 8. public static void main(String[] args) throws IOException { 9. 10. // Leer el Codigo Fuente de este Archivo 11. BufferedReader in = new BufferedReader( 12. new FileReader("LeerArchivo.java")); 13. String s, s2 = new String(); 14. while ((s = in.readLine()) != null) 15. s2 += s + "\n"; 16. in.close(); 17. 18. // Leer Datos Interactivamente 19. BufferedReader stdin = new BufferedReader( 20. new InputStreamReader(System.in)); 21. System.out.print("Introduzca cualquier dato: "); 22. s2 += "Usted agrego la linea \" " + stdin.readLine() + 23. " \" en la linea de comandos"; 24. 25. 26. // Depositar Datos en un Archivo de Texto 27. try { 28. BufferedReader leer = new BufferedReader( 29. new StringReader(s2)); 30. PrintWriter escribir = new PrintWriter( 31. new BufferedWriter(new FileWriter("Archivo_Stream.txt"))); 32. int lineaNo = 1; 33. while ((s = leer.readLine()) != null) 34. escribir.println(lineaNo++ + ": " + s); 35. escribir.close(); 36. } catch (EOFException e) { 37. System.out.println("Final de Stream"); 38. } 39. } 40. }
Clase LeerArchivo
Esta Clase esta diseada para leer el archivo de Cdigo Fuente presente, leer un rengln proporcionado al momento de ejecutarse el programa y finalmente depositar todo el contenido en otro archivo; al igual que el ejemplo anterior consta nicamente de su mtodo principal (main) y un bloque try/catch.
Lectura de Datos
En la primer seccin es definida una Clase BufferedReader, la cual lee el archivo de Cdigo Fuente, ntese que no es necesario colocar esta declaracin dentro de un bloque try/catch puesto que ya se sabe que el archivo existe. Se define un ciclo while que permanecer activo hasta que el resultado del mtodo readLine() sobre la referencia definida anteriormente equivalga a null. NOTA: El mtodo readLine retorna un resultado null ya que el archivo no contenga lineas. Dentro de cada iteracin, es concatenado cada rengln ledo del "Stream" y colocado en la variable s2. Es definida otra Clase BufferedReader la cual es empleada para datos introducidos de la consola (System.in). El valor introducido por el usuario es concatenado a la variable s2 que contiene el archivo de cdigo fuente.
Escritura de Datos
Se inicia un Bloque try/catch. Se declara una instancia de la Clase BufferedReader , la cual recibe como parmetro de entrada el String definido en la seccin de lectura. Es definida una Clase PrintWriter que permite escritura de "Streams", esta Clase recibe como parmetro de entrada la Clase BufferedWriter la cual a su vez recibe la Clase FileWriter, esta ltima toma como parmetro el archivo que ser generado (Archivo_Stream.txt). Se genera un ciclo while, el cual en cada iteracin asigna los valores de cada rengln ledo hacia la referencia de la Clase PrintWriter. Se define un error ("Exception") del tipo EOFException el cual sera generado en caso de llegar a un fin prematuro del archivo.
Si vamos a escribir en un fichero de texto, sera una buena prctica el validar si dicho fichero existe o no. Tambin podramos escribir sobre el fichero directamente. Ahora que esto lo haramos si no nos importase mucho el contenido que este tuviese. Para validar si existe un fichero podemos leer el ejemplo Saber si existe un fichero con Java. Dicho artculo nos explica de forma detallada lo simple que esto resulta. Resumiendo, bastar utilizar el mtodo exists() de la clase File.
1. String sFichero = "fichero.txt"; 2. File fichero = new File(sFichero); 3. 4. if (fichero.exists()) {...}
En el caso de que no exista el fichero, nos pondremos manos a la obra para escribir sobre el fichero. Lo primero que tendremos que hacer ser crear un BufferedWriter. Esta clase nos ayuda a manejar los stream en forma de buffer con mtodos muy sencillos. Este buffer necesitar saber cual es el fichero. Esto se lo proporcionamos desde la clase FileWriter.
Recuerda que la filosofa de acceso a la lectura y escritura a los ficheros, siempre es la misma. Buffer, que envuelve a Reader, que a su vez envuelve al fichero (o stream). El cdigo nos quedara algo as:
1. BufferedWriter bw = new BufferedWriter(new FileWriter(sFichero));
Para escribir texto nos apoyamos en el mtodo .write() del buffer. Muy sencillo. La verdad es que no se me ocurra que texto grabar sobre el fichero, a si que, al final, vamos a volcar 10 lneas iguales ayudndonos de un bucle for.
1. for (int x=0;x<10;x++) 2. bw.write("Fila numero " + x + "\n");
No nos podemos olvidar de dos ltimas cosas. Siempre que manejemos el fichero para escribir deberemos de cerrarle con el mtodo .close(). Y lo otro, que toda la operativa de la clase java.io.* nos avisar de sus problemas de acceso a fichero, elevndonos una excepcin IOException. Que habr que capturar en alguna parte de nuestro cdigo.