Está en la página 1de 13

Cuando tratamos con estructuras de tipo stream para realizar lecturas y escrituras, los pasos habituales son el ir apoyndonos

en clases que nos faciliten el manejo del dispositivo de destino, desde la de ms alto nivel a la de menor nivel. Lo que tcnicamente denominaramos wrapping. Para manejar cadenas de texto (Java String), la estructura que nos ayuda, en Java IO, a ello es el BufferedReader. Pero a este nivel, todava no hemos identificado el dispositivo de destino. En la lectura de ficheros de texto, lo primero que tenemos que hacer es crear un Reader de tipo fichero. Esto es encapsulado en la clase FileReader. Ser esta clase la que utilice el buffer como origen de lectura. Quedndonos estas simples lneas:
FileReader fr = new FileReader("datos.txt"); BufferedReader bf = new BufferedReader(fr);

O en un formato mas "tcnico" podramos irnos a tener una sola linea de codigo:
BufferedReader bf = new BufferedReader(new FileReader("datos.txt"););

Ahora solo nos queda es ir leyendo del fichero. Para ello utilizaremos los mtodos de BufferedReader. Y en concreto el que nos va dando lnea a lnea readLine(), La lectura se har de forma repetitiva hasta que la lectura de la lnea sea nula.
while ((sCadena = bf.readLine())!=null) { System.out.println(sCadena); }

Hay que tener especial cuidado con el manejo de las excepciones. Todo manejo con la librera java.io nos har estar expuestos al manejo de la excepcin IOException. Y tampoco podemos olvidar que a la hora de realizar la lectura de un fichero, este, no exista. En ese caso se lanzar una excepcin FileNotFoundException.

Introduccin
Las entradas y salidas (E/S) en Java utilizan streams (secuencias o flujos). Un flujo representa una corriente de datos con (al menos) un escritor y un lector. Por ejemplo, cuando leemos del teclado estamos usando una corriente de entrada donde el programa es el lector. Un flujo importante en este tipo de problemas es InputStream, que es una clase abstracta para el flujo de entradas de bytes sin estructura. Es una superclase de todas las dems clases de flujo de entrada y proporciona una interfaz bsica. El sistema Java incluye un InputStream bsico para entradas por teclado: System.in. Pero necesitamos algo ms sofisticado que un sistema de lectura de bytes sin estructura. Para ello vamos a utilizar flujos "filtro" que sirven de intermediarios: manejan un flujo de entrada de bytes y lo convierten en flujos estructurados (modo caracter). Un ejemplo de filtro es InputStreamReader, que sirve de puente desde un stream de bytes a un stream de caracteres:

InputStreamReader isr = new InputStreamReader( System.in );

Con un InputStreamReader tenemos el mtodo read() para leeer caracteres. Pero todava no es suficiente. Necesitamos otros "puentes" que conviertan el flujo en modo carcter a uno en modo buffer (una especie de cadena de caracteres). Para ello contamos con clases como BufferedReader que recibe en su constructor el flujo de caracteres y maneja el flujo en la forma de buffer, con un mtodo como readLine() que lee los caracteres hasta encontrar un final de lnea. Un ejemplo de estas transformaciones (de flujo de byte a flujo de carcter y de ste a buffer) se encuentra en el manejo de teclado:

BufferedReader entrada = new BufferedReader(new InputStreamReader(System.in)); entrada.readLine();

Archivos y URL
Lo que hemos visto aplicado a un flujo de teclado es aplicable a los archivos. En el siguiente ejemplo el flujo de entrada es un archivo (clase java.io.File):

File f = new File( "esto.txt" ); BufferedReader entrada = new BufferedReader( new FileReader( f ) ); if ( f.exists() ) entrada.readLine();

Pero en nuestro caso vamos a manejarnos en un contexto web, por tanto nos interesa usar una URL (Uniform Resource Locator). Una URL apunta a un recurso, que es una forma genrica para hablar de un objeto en espacio web (un archivo, un directorio, un host, un servidor, etc.):

URL url = new URL( "archivo.txt" ); Abro URL

//

InputStream is = url.openStream(); // Abro InputStream desde URL BufferedReader di = new BufferedReader(new InputStreamReader( is )); di.readLine();

El problema en primer lugar es situar al archivo con respecto al applet. Para ello nos vamos a ayudar de una serie de mtodos de la clase Applet: y y getDocumentBase(): devuelve URL absoluto de la pgina que invoca al applet. getCodeBase(): devuelve URL absoluto del directorio especificado en el parmetro CODEBASE de la pgina. CODEBASE se referencia a partir del directorio donde se encuentra la pgina HTML. Puesto que usualmente CODEBASE=".", por tanto el resultado es que getCodeBase() es el directorio donde est el archivo html. En el siguiente ejemplo CODEBASE es "/xx/yy/df/", ya que el archivo html est en "/xx/yy/": <applet codebase = "df" ....

y y y y

getClass().getResource( String recurso ): devuelve URL absoluto del recurso sealado en el argumento. Conviene tener en cuenta dos aspectos: el primero es que este mtodo toma como directorio de partida (o de referencia) el del applet. El segundo aspecto interesante es que si no encuentra el recurso devuelve null.

A continuacin puede ver el cdigo fuente de un applet que usa estos mtodos (editor es un objeto de la clase TextArea):

private void jbInit() throws Exception { editor.setBackground( Color.orange ); editor.append( "getName(): " + this.getName() + "\n" ); editor.append( "getClass().getName(): " + getClass().getName() + "\n" ); editor.append( "Protocol: " + this.getDocumentBase().getProtocol() + "\n"); editor.append( "CodeBase (getFile): " + this.getCodeBase().getFile() + "\n"); editor.append( "CodeBase (toString): " + this.getCodeBase().toString() + "\n"); editor.append( "DocumentBase (getFile): " + this.getDocumentBase().getFile() + "\n"); editor.append( "DocumentBase (toString): " + this.getDocumentBase().getFile() + "\n");

editor.append( "Habr podido ver que getCodeBase().getFile() y getCodeBase().toString()" + " no dan el mismo resultado\n\n"); /*** Obtengo la URL, si no se encuentra el recurso, entonces la url es null ***/ URL url = getClass().getResource("applet_informador.class"); if (url == null) editor.append( "Dont find Resource applet_informador.class. \n"); else editor.append( "Resource (\"applet_informador.class\"): " + url.toString() + "\n"); editor.append( "getClass().getResource(\"xx\") tiene como directorio de referencia el del applet\n\n"); /******* Lee el archivo de cdigo fuente y lo escribe en el editor *********/ editor.append( "CODIGO FUENTE:\n"); if ( !leer_archivo( "codigo_fuente/applet_informador.java", editor) ) editor.append( "No he podido abrir el archivo de cdigo fuente"); this.setLayout(new BorderLayout()); this.add( editor, BorderLayout.CENTER ); }

Lectura de un fichero de texto en java


Podemos abrir un fichero de texto para leer usando la clase FileReader. Esta clase tiene mtodos que nos permiten leer caracteres. Sin embargo, suele ser habitual querer las lneas completas, bien porque nos interesa la lnea completa, bien para poder analizarla luego y extraer campos de ella. FileReader no contiene mtodos que nos permitan leer lneas completas, pero s BufferedReader. Afortunadamente, podemos construir un BufferedReader a partir del FileReader de la siguiente forma:
File archivo = new File ("C:\\archivo.txt"); FileReader fr = new FileReader (archivo); BufferedReader br = new BufferedReader(fr); ... String linea = br.readLine();

La apertura del fichero y su posterior lectura pueden lanzar excepciones que debemos capturar. Por ello, la apertura del fichero y la lectura debe meterse en un bloque try-catch.

Adems, el fichero hay que cerrarlo cuando terminemos con l, tanto si todo ha ido bien como si ha habido algn error en la lectura despus de haberlo abierto. Por ello, se suele poner al try-catch un bloque finally y dentro de l, el close() del fichero. El siguiente es un cdigo completo con todo lo mencionado.
import java.io.*; class LeeFichero { public static void main(String [] arg) { File archivo = null; FileReader fr = null; BufferedReader br = null; try { // Apertura del fichero y creacion de BufferedReader para poder // hacer una lectura comoda (disponer del metodo readLine()). archivo = new File ("C:\\archivo.txt"); fr = new FileReader (archivo); br = new BufferedReader(fr); // Lectura del fichero String linea; while((linea=br.readLine())!=null) System.out.println(linea); } catch(Exception e){ e.printStackTrace(); }finally{ // En el finally cerramos el fichero, para asegurarnos // que se cierra tanto si todo va bien como si salta // una excepcion. try{ if( null != fr ){ fr.close(); } }catch (Exception e2){ e2.printStackTrace(); } } } }

Como opcin para leer un fichero de texto lnea por lnea, podra usarse la clase Scanner en vez de el FileReader y el BufferedReader. Ver el ejemplo del Ejemplo de lectura de un fichero con Scanner

Escritura de un fichero de texto en java


El siguiente cdigo escribe un fichero de texto desde cero. Pone en l 10 lneas
import java.io.*; public class EscribeFichero {

public static void main(String[] args) { FileWriter fichero = null; PrintWriter pw = null; try { fichero = new FileWriter("c:/prueba.txt"); pw = new PrintWriter(fichero); for (int i = 0; i < 10; i++) pw.println("Linea " + i); } catch (Exception e) { e.printStackTrace(); } finally { try { // Nuevamente aprovechamos el finally para // asegurarnos que se cierra el fichero. if (null != fichero) fichero.close(); } catch (Exception e2) { e2.printStackTrace(); } } } }

Si queremos aadir al final de un fichero ya existente, simplemente debemos poner un flag a true como segundo parmetro del constructor de FileWriter.
FileWriter fichero = new FileWriter("c:/prueba.txt",true);

Ficheros binarios
Para ficheros binarios se hace exactamente igual, pero en vez de usar los "Reader" y los "Writer", se usan los "InputStream" y los "OutputStream". En lugar de los readLine() y println(), hay que usar los mtodos read() y write() de array de bytes. El siguiente ejemplo hace una copia binaria de un fichero
package chuidiang.ejemplos; import import import import java.io.BufferedInputStream; java.io.BufferedOutputStream; java.io.FileInputStream; java.io.FileOutputStream;

public class CopiaFicheros { public static void main(String[] args) { copia ("c:/ficheroOrigen.bin", "c:/ficheroDestino.bin"); } public static void copia (String ficheroOriginal, String ficheroCopia)

{ try { // Se abre el fichero original para lectura FileInputStream fileInput = new FileInputStream(ficheroOriginal); BufferedInputStream bufferedInput = new BufferedInputStream(fileInput); // Se abre el fichero donde se har la copia FileOutputStream fileOutput = new FileOutputStream (ficheroCopia); BufferedOutputStream bufferedOutput = new BufferedOutputStream(fileOutput); // Bucle para leer de un fichero y escribir en el otro. byte [] array = new byte[1000]; int leidos = bufferedInput.read(array); while (leidos > 0) { bufferedOutput.write(array,0,leidos); leidos=bufferedInput.read(array); } // Cierre de los ficheros bufferedInput.close(); bufferedOutput.close(); } catch (Exception e) { e.printStackTrace(); } } }

Los Buffered*
Si usamos slo FileInputStream, FileOuputStream, FileReader o FileWriter, cada vez que hagamos una lectura o escritura, se har fisicamente en el disco duro. Si escribimos o leemos pocos caracteres cada vez, el proceso se hace costoso y lento, con muchos accesos a disco duro. Los BufferedReader, BufferedInputStream, BufferedWriter y BufferedOutputStream aaden un buffer intermedio. Cuando leamos o escribamos, esta clase controlar los accesos a disco.
y y

Si vamos escribiendo, se guardar los datos hasta que tenga basantes datos como para hacer la escritura eficiente. Si queremos leer, la clase leer muchos datos de golpe, aunque slo nos d los que hayamos pedido. En las siguientes lecturas nos dar lo que tiene almacenado, hasta que necesite leer otra vez.

Esta forma de trabajar hace los accesos a disco ms eficientes y el programa correr ms rpido. La diferencia se notar ms cuanto mayor sea el fichero que queremos leer o escribir.

.2.1. Archivo de texto


Archivos en Java Para poder entender como manejar un archivo en Java debemos empezar por entender lo que es un Flujo de datos. Flujos Un flujo es el sistema que nos ayuda a realizar la comunicacin en Java, utilizando el paquete ya implementado java.io cuyo fin es guardar y tomar la informacin en cada uno de los diversos dispositivos de almacenamiento. Se puede decir que un flujo es como un tubo o recipiente en donde podemos leer o escribir bytes. De un extremo nosotros ponemos algo y del otro extremo del tubo puede estar cualquier dispositivo un teclado, un monitor, un archivo, un objeto de Java, etc. Todos los flujos que aparecen en Java englobados en el paquete java.io, pertenecen a dos clases abstractas comunes: java.io.InputStream para los flujos de Entrada (aquellos de los que podemos leer) y java.io.OutputStream para los flujos de salida (aquellos en los que podemos escribir). Java tiene un conjunto de Clases y mtodos ya establecidos para captar la informacin de los flujos de entrada y de salida por los dispositivos estndar. En el caso de los flujos de entrada tiene System.in, el cual suele recibir los datos de teclado, utilizando el mtodo read() para leer los caracteres. Para los flujos de salida se utiliza System.out y los datos se envan a pantalla, utilizando el mtodo print() o println() cuya diferencia es que con el print la informacin se manda tal cual al buffer de salida, sin saltar de lnea, pero debemos utilizar el mtodo flush() para saltar de lnea, en cambio con el println se manda el buffer de salida directamente a la pantalla y se salta de lnea. Existe un flujo de datos para los errores y ste es el System.err, el cual enva la salida tambin directamente a la pantalla, pero si se desea se puede redireccionar, de manera que se separe el dispositivo de salida del dispositivo de la salida con error. La manera en la que en Java se toma la informacin de entrada es asociando al flujo estandar de entrada la creacin de un objeto de InputStreamReader, el cual es utilizado a su vez para la creacin de un objeto de la clase BufferedReader, de esta manera lo que viene del teclado se envuelve entre clases para de pasar de bits a bytes

y luego a datos que pueden ser ledos. Al utilizar el objeto de la clase BufferedReader tenemos el mtodo readLine() el cual lee un conjunto de bytes del buffer de entrada hasta detectar el fin de lnea. La manera en la que Java saca la informacin a salida es utilizando la clase PrintWriter tomando el objeto de salida System.out, para crear el objeto de la clase PrintWriter. Los mtodos que se utilizan son el print y println. Las clases de Streams, Readers y Writers en java ven la entrada y salida como una secuencia de bytes. Los streams de bajo nivel ms comunes son: y y y y FileInputStream(String pathname) FileInputStream(File file) FileOutputStream(String pathname) FileOutputStream(File file)

Una vez que un stream de entrada ha sido construido, pueden llamarse mtodos para leer un simple byte, o una porcin de un arreglo de bytes. A continuacin un ejemplo que lee bytes de un archivo. byte b; byte bytes[] = new byte[100]; byte morebytes[] = new byte[50]; try { FileInputStream fis = new FileInputStream(nombre_del_archivo); b=(byte)fis.read(); // lee un byte fis.read(bytes); //llena el arreglo fis.read(morebytes, 0, 20); //lee 20 elementos } catch (IOException e) {} Es conveniente leer bytes de un dispositivo de entrada y escribir a un dispositivo de salida. Sin embargo, normalmente lo que se desea leer y escribir no son bytes sino informacin tal como enteros o cadenas de caracteres (int o String), etc. Java cuenta con manejo de streams de alto nivel. Los ms comunes son: y y DataInputStream(InputStream instream) DataOutputStream(OutputStream outstream)

Un ejemplo de cmo grabar en un archivo utilizando un DataOutputStream sera: try { //Construye la cadena de salida FileOutputStream fos = new FileOutputStream("nombre_archivo"); DataOutputStream dos = new DataOutputStream(fos); //lee dos.writeDouble(123.456); dos.writeInt(55); dos.writeUTF("Mary tiene un pequeo borreguito"); //cierra dos.close(); fos.close(); } catch (IOException e) {} Un ejemplo que muestra como leer los datos que el anterior ejemplo dej, utilizando un DataInputStream sera: try { //Construye la cadena de entrada FileInputStream fis = new FileInputStream("nombre_archivo"); DataInputStream dis = new DataInputStream(fis); //lee double d = dis.readDouble(); int i = dis.readInt(); String s = dis.readUTF(); //cierra dis.close();

fis.close(); } catch (IOException e) {} Puedes probar implementar estas instrucciones cada conjunto en una diferente aplicacin y ver lo que hacen, para que percibas como es que se genera el archivo y luego lo leas desde la otra aplicacin. Otros manejadores de streams de alto nivel: BufferedInputStream y BufferedOutputStream, manejan internamente un buffer de manera que los bytes puedan ser escritos y ledos en bloques, optimizando el proceso de entrada/salida. BufferedReader(Reader reader) PrintStream: Esta clase maneja texto o primitivas de datos. Las primitivas de datos se convierten a representaciones de carcter. El System.out y System.err que se utiliza en las aplicaciones de consola son ejemplos de esta clase. PrintStream(OutputStream out) Al igual que los streams de entrada y salida: Los readers y writers de bajo nivel se comunican con dispositivos, mientras que los de alto nivel se comunican con los de bajo nivel. La diferencia es que los readers y writers se orientan exclusivamente al manejo de caracteres Unicode. Un ejemplo de reader de bajo nivel es el FileReader: y FileReader(String pathname) y FileReader(File file)

Algunos mtodos para lectura que provee la superclase Reader son: int read() throws IOException. Regresa el siguiente valor entero del carcter (16 bits: 0 to 65535), -1 si no hay ms caracteres. int read(char[] cbuf) throws IOException. Llena el arreglo con los caracters leidos, regresa el nmero de caracteres que se leyeron. Un ejemplo de writer de bajo nivel es el FileWriter: y FileWriter(String pathname) y FileWriter(File file)

BufferedReader y BufferedWriter. Estas clases tienen buffers internos de manera que los datos pueden ser ledos o escritos en bloques. Son similares a

BufferedInputStream y BufferedOutputStream, pero mientras stos manejan bytes, BufferedReader y BufferedWriter se maneja con caracteres. Constructores para estas clases: BufferedReader(Reader in) BufferedWriter(Writer out) InputStreamReader y OutputStreamWriter. Estas clases convierten entre streams de bytes y secuencias de caracteres Unicode. Provee el puente entre la conversin de los bytes y un sistema (charset). Ejemplo: BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in)); PrintWriter. Similar a PrintStream, pero escribe caracteres en vez de bytes. Ejemplo. PrintWriter stdErr = new PrintWriter(System.out,true); A continuacin tenemos un ejemplo muy sencillo: import java.io.*; public class AplicacionStreams { public static void main(String[] args) throws IOException { BufferedReader ent = new BufferedReader(new InputStreamReader(System.in)); PrintWriter sal = new PrintWriter(System.out, true); PrintWriter salErr = new PrintWriter(System.err, true); salErr.println("Da el nmero"); int numero = Integer.parseInt(ent.readLine()); if (numero < 0) { salErr.println("Error numero negativo"); } else { sal.println("" + (Math.pow(numero,2)));

} } }