Un paréntesis de Entrada/Salida

En Java hay muchas clases para leer y escribir archivos (u otros dispositivos de E/S). Están reunidos en la biblioteca java.io.

Vamos a empezar como siempre con un pequeño ejemplo funcional y en seguida nos meteremos en el necesario camino de las excepciones...

Primera Lectura

// archivo: Ejemplo9.java - compilar con "javac Ejemplo9.java", etc. etc.
import java.io.*;

public class Ejemplo9 {
public static void main(String args[]) throws FileNotFoundException,IOException {
FileInputStream		fptr;
DataInputStream	f;
String			linea = null;
    
fptr = new FileInputStream("Ejemplo9.java");
f = new DataInputStream(fptr);
do {
linea = f.readLine();
if (linea!=null) System.out.println(linea);
} while (linea != null);
fptr.close();
}
}


(Caramba! ¿Qué hace ese throws ahí?)

El programa de ejemplo simplemente lee un archivo de texto y lo muestra en pantalla, algo así como el type del DOS o el cat de Unix.

Dejemos por ahora el throws FileNotFoundException,IOException y vamos al código.

fptr = new FileInputStream("Ejemplo9.java");



La clase FileInputStream (descendiente de InputStream) nos sirve para referirnos a archivos o conexiones (sockets) de una máquina. Podemos accederlos pasando un String como aquí, un objeto de tipo File o uno de tipo FileDescriptor, pero en esencia es lo mismo. Al crear un objeto de este tipo estamos "abriendo" un archivo, clásicamente hablando.

Si el archivo no existe (por ejemplo reemplacen "Ejemplo9.java" por alguna otra cosa, como "noexiste.txt"), al ejecutarlo nos aparece un error:

C:\java\curso>java Ejemplo9
java.io.FileNotFoundException: noexiste.txt
        at java.io.FileInputStream.<init>(FileInputStream.java:51)
        at Ejemplo9.main(Ejemplo9.java:9)


(Caramba! ¿Dónde vi ese FileNotFoudException antes?)

Justamente, cuando el archivo al que quiero acceder no existe, Java "lanza" una excepción. Esto es, un aviso de que algo falló y, si no se toma ninguna acción, detiene el programa.

La clase FileInputStream puede "lanzar" (throws) la excepción FileNotFoundException.

¿Cómo capturar y tratar las excepciones? En seguida; primero terminemos con nuestro programa.

f = new DataInputStream(fptr);


La clase DataInputStream nos permite leer, en forma independiente del hardware, tipos de datos de una "corriente" (stream) que, en este caso, es un archivo. Es descendiente de FilterInputStream e implementa DataInput, una interface.

Al crear un objeto de tipo DataInputStream lo referimos al archivo, que le pasamos como parámetro (fptr); esta clase tiene toda una serie de métodos para leer datos en distintos formatos.

En nuestro programa usamos uno para leer líneas, que devuelve null cuando se llega al final del archivo o un String con el contenido de la línea:

do {
      linea = f.readLine();
      System.out.println(linea);
} while (linea != null);


En seguida de leer la línea la imprimimos, y repetimos esto mientras no nos devuelva null.

Al final, cerramos el archivo:

fptr.close();


Tanto readLine como close pueden lanzar la excepción IOException, en caso de error de lectura o cierre de archivo.

En realidad, podríamos no haber usado un DataInputStream y trabajar en forma más directa:

import java.io.*;

public class Ejemplo10 {
public static void main(String args[]) throws FileNotFoundException,IOException {
FileInputStream		fptr;
int			n;    

fptr = new FileInputStream("Ejemplo9.java");
do {
n = fptr.read();
if (n!=-1) System.out.print((char)n);
} while (n!=-1);
fptr.close();
}
}


Ya que la clase FileInputStream también dispone de métodos para leer el archivo. Sólo que son unos pocos métodos que nos permiten leer un entero por vez o un arreglo de bytes. DataInputStream tiene métodos para leer los datos de muchas formas distintas, y en general resulta más cómodo.

Capturando excepciones

Ahora sí, vamos a ver cómo nos las arreglamos con las excepciones para que no se nos pare el programa con un mensaje tan poco estético...

En lugar de lanzar las excepciones al intérprete, vamos a procesarlas nosotros mediante la cláusula catch:

	// Archivo:	Ejemplo11.java
	// Compilar con:	javac Ejemplo11.java
	// Ejecutar con:	java Ejemplo11 <nombre_archivo>
import java.io.*;

public class Ejemplo11 {
public static void main(String args[]) {
FileInputStream		fptr;
DataInputStream	f;
String			linea = null;

try {
fptr = new FileInputStream(args[0]);
f = new DataInputStream(fptr);
do {
linea = f.readLine();
if (linea!=null) System.out.println(linea);
} while (linea != null);
fptr.close();
}
catch (FileNotFoundException e) {
System.out.println("Hey, ese archivo no existe!\n");
}
catch (IOException e) { 
System.out.println("Error de E/S!\n");
}
}
}


También hicimos un cambio para elegir el archivo a imprimir desde la línea de comandos, en lugar de entrarlo fijo, utilizando para eso el argumento del método main(arg[]), que consiste en una lista de Strings con los parámetros que se pasan en la línea a continuación de java nombre_programa. Por ejemplo, si llamamos a este programa con:

		java Ejemplo11 archi.txt otro.xxx


arg[0] contendrá "archi.txt", arg[1] contendrá "otro.xxx", y así sucesivamente.

Por supuesto, si llamamos a Ejemplo11 sin parámetros se lanzará otra excepción al intentar accederlo:

C:\java\curso>java Ejemplo11
java.lang.ArrayIndexOutOfBoundsException: 0
        at Ejemplo11.main(Ejemplo11.java:10)


Pero también podríamos capturarla!

Veamos un poquito cómo es esto de capturar excepciones.

La cláusula try engloba una parte del programa donde se pueden lanzar excepciones. Si una excepción se produce, Java busca una instrucción catch (nombre_de_la_excepción variable), y, si la encuentra, ejecuta lo que ésta engloba. Si no encuentra un catch para esa excepción, para el programa y muestra el error que se produjo.

Por ejemplo, para evitar este último error bastaría con agregar:

catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Debe ingresar un nombre de archivo!");
System.out.println("Ej.: java Ejemplo11 pepe.txt");
}


Hay que notar que cuando se lanza una excepción el programa igual se detiene, porque el código que sigue al lanzamiento de la excepción no se ejecuta. Veremos luego cómo se comporta esto en un objeto que fue creado por otro, y cómo usar la instrucción finally para poner una parte de código que se ejecute pase lo que pase.

Los applets y los archivos

Veamos cómo se comporta esta aplicación si la modificamos para usarla como applet.

/*
// ----- Archivo:	Ejemplo12.java
*/

import java.io.*;
import java.awt.*;
import java.applet.*;

public class Ejemplo12 extends Applet {

public void init() {
		new Ventana12();
}
}

/*
// -------- Esta clase es la que en realidad hace el trabajo
*/

class Ventana12 extends Frame {

TextArea	contenido;
Button		cerrar;

Ventana12() {
super("Ejemplo de E/S");
contenido = new TextArea();
cerrar = new Button("Cerrar");    
CargarArchivo();
add("North",contenido);
add("South",cerrar);
pack();
show();
}

public boolean handleEvent(Event e) {
if ((e.id==Event.WINDOW_DESTROY)||(e.target==cerrar))
dispose();
return super.handleEvent(e);
}

void CargarArchivo() {
FileInputStream		fptr;
DataInputStream	f;
String			linea = null;
try {
fptr = new FileInputStream("Ejemplo12.java");
f = new DataInputStream(fptr);
do {
linea = f.readLine();
if (linea!=null)
contenido.appendText(linea+"\n");
} while (linea != null);
fptr.close();
}
catch (FileNotFoundException e) {
contenido.appendText("Hey, ese archivo no existe!\n");
}
catch (IOException e) { 
contenido.appendText("Error de E/S!\n");
}
}
}


Lo cargamos desde la página Ejemplo12.html:

<HTML>
<HEAD>
<TITLE>Ejemplo 12 - Ejemplo con archivo</TITLE>
</HEAD>
<BODY>
<applet code="Ejemplo12.class" width=170 height=150>
</applet>
</BODY>
</HTML>


Mientras corramos esto en la misma máquina, no hay problema (anda muy bien!). Pero qué pasa si intentamos cargarlo desde la red? Para los que no tengan server html puse una copia en:

http://www.amarillas.com/rock/java/Ejemplo12.htm

El archivo no aparece! En su lugar se produce una excepción; en la línea de estado del Microsoft Internet Explorer, por ejemplo, se lee:

exception: com.ms.applet.AppletSecurityException: security.file.read: Ejemplo12.java



Esto es debido a una restricción de seguridad de Java: NO SE PUEDEN CARGAR ARCHIVOS QUE ESTEN EN UNA MAQUINA DISTINTA A AQUELLA DESDE LA CUAL SE CARGO EL APPLET. El applet se corre en el cliente, e intenta acceder a un archivo local. Eso es lo que provoca la excepción (que, por supuesto, puede detectarse con un catch y tratarse...)

Por cuestiones de seguridad, los applets son más limitados que las aplicaciones Java locales. Las políticas de seguridad las manejan los browsers (no Java), y generalmente los límites que se imponen a los applets son:

Un applet no puede cargar bibliotecas (libraries) ni definir métodos nativos
No puede leer o escribir normalmente archivos en el cliente que lo carga desde otro server
No puede establecer conexiones de red, salvo al servidor del que proviene
No puede arrancar programas en la máquina donde se está ejecutando
No puede leer ciertas propiedades del sistema
En las ventanas de los applets se indica que se trata de un applet

Sin embargo, pueden:

Reproducir sonidos
Pueden establecer conexiones con el servidor del que provienen
Pueden llamar fácilmente páginas HTML desde el browser
Pueden invocar métodos públicos de otros applets de la misma página
Si se cargan desde la propia máquina (localmente) no tienen ninguna de las restricciones anteriores
Pueden seguir corriendo aunque se cambie de página en el browser

En realidad, la especificación de Java permite que los applets lean archivos en otras máquinas dando la URL completa; sin embargo, los browsers no lo permiten. Veremos más adelante cómo intercambiar datos entre máquinas para poder ver un archivo del server, por ejemplo.

Nuestro modesto "Editor"

Para terminar este capítulo, el siguiente applet nos permite cargar, editar y grabar archivos ascii a elección. Podemos usar inclusive las acciones "cut & paste" del windows manager (Ctrl-C y Ctrl-V en Windows)!

Cargarlo con "appletviewer Ejemplo13" luego de haberlo compilado (o usar una página html desde un browser):


/*
// ----- Archivo:	Ejemplo13.java
*/

import java.io.*;
import java.awt.*;
import java.applet.*;

public class Ejemplo13 extends Applet {

  public void init() {
	new Ventana13();
  }
}

/*
// -------- Esta clase es la que en realidad hace el trabajo
*/

class Ventana13 extends Frame {

  TextArea	contenido;
  Botones13	pieVentana;

  Ventana13() {
    super("Ejemplo de E/S");
    contenido = new TextArea();
    pieVentana = new Botones13();    
    add("North",contenido);
    add("South",pieVentana);
    pack();
    show();
  }

  public boolean handleEvent(Event e) {
    if ((e.id==Event.WINDOW_DESTROY)||(e.id==2003))
       dispose();
    if (e.id==2001) CargarArchivo(pieVentana.toString());
    if (e.id==2002) GrabarArchivo(pieVentana.toString());
    return super.handleEvent(e);
  }

  void CargarArchivo(String nombre) {
    FileInputStream	fptr;
    DataInputStream	f;
    String		linea = null;
    contenido.setText("");
    try {
      fptr = new FileInputStream(nombre);
      f = new DataInputStream(fptr);
      do {
        linea = f.readLine();
        if (linea!=null) contenido.appendText(linea+"\n");
      } while (linea != null);
      fptr.close();
    }
    catch (FileNotFoundException e) {
	new Error13("El archivo no existe!");
    }
    catch (IOException e) { 
	new Error13("Error leyendo archivo!");
    }
  }

  void GrabarArchivo(String nombre) {
    FileOutputStream	fptr;
    DataOutputStream	f;
    try {
      fptr = new FileOutputStream(nombre);
      f = new DataOutputStream(fptr);
      f.writeBytes(contenido.getText());
      fptr.close();
    }
    catch (IOException e) { 
	new Error13("Error grabando archivo!");
    }
  }
}

/*
// -------- Esta es para los botones y el nombre del archivo
*/

class Botones13 extends Panel {

  TextField	fname;
  Button	cargar;
  Button	grabar;
  Button	cerrar;

  Botones13() {
    setLayout(new GridLayout(1,4));
    fname = new TextField();
    cargar = new Button("Cargar");
    grabar = new Button("Grabar");
    cerrar = new Button("Cerrar");
    add(new Label("Archivo:"));
    add(fname);
    add(cargar);
    add(grabar);
    add(cerrar);
  }

  public boolean handleEvent(Event e) {
    if ((e.id==Event.ACTION_EVENT)&&(e.target==cargar))
	e.id=2001;
    if ((e.id==Event.ACTION_EVENT)&&(e.target==grabar))
	e.id=2002;
    if ((e.id==Event.ACTION_EVENT)&&(e.target==cerrar))
	e.id=2003;
    return super.handleEvent(e);
  }

  public String toString() {
    return fname.getText();
  }
}

/*
// ------- Para mostrar los errores...
*/

class Error13 extends Frame {
  
  Error13(String error) {
	add("Center",new Label(error));
	add("South", new Button("Ok"));
	pack();
	show();
  }

  public boolean handleEvent(Event e) {
    dispose();
    return super.handleEvent(e);
  }
}


Un poco largo... pero vale la pena, ¿no?!

 

<> Página de tutoriales <> Página Anterior <> Siguiente Capitulo <>