Entrada y Salida

6 Entrada y Salida

La entrada y salida en java se implementa en el paquete java.io y toto el código presentado en este capítulo se importa, aunque no se incluya la correspondiente sentencia import.

La E/S en java se basa en el concepto de flujo, que es una secuencia ordenada de datos que tienen una fuente (flujos de entrada) o un destino (flujos de salida). Las clases de E/S aislan a los programadores de los detalles específicos del sistema de funcionamiento de la máquina, al tiempo que posibilitan el acceso a recursos del sistema por medio de ficheros o archivos (files).

En este capítulo vamos a ocuparnos de las clases de E/S más sencillas, dejando las otras más sofisticadas para cuando tengamos necesidad de utilizarlas.

6.1 Entrada básica: la clase InputStream

La clase abstracta InputStream declara los métodos para leer datos desde una fuente concreta y es la clasa base de la mayor parte de los flujos de entrada en java.io. Soporta los métodos siguientes:

El siguiente ejemplo es un programa muy sencillo que hace el eco del la entrada estandar (el teclado) leyendo los caracteres de 5 en 5 (el return también es un carácter). Finaliza cuando se cierra el flujo de entrada con un carácter Ctrl-D. El método main debe indicar que lanzará una excepción InputStream. El método read se aplica sobre el objeto System.in que es un flujo de entrada; la entrada estandar, el teclado. El constructor String(byte[] bytes, int hiByte, int offset, int conunt) hace un String nuevo cuyo valor es el subarray especificado del array bytes comenzando en offset y de count caracteres. En las conversiones de ASCII8 o ISO-LATIN-1 el hibyte estará a 0.

import java.io.*;
 
class Eco{
public static void main(String [] args)
   throws IOException{
 
   byte [] b= new byte[5];
   String cadena;
   int l=0;
   for(;;){
      l=System.in.read(b);
      if (l<0) break;
      cadena= new String(b,0,0,l);
      System.out.print("/"+cadena+"/");
   }
}}

6.2 Salida básica: la clase OutputStream

La clase abstracta OutputStream es análoga a InputStream sólo que proporciona métodos para manejar el flujo de salida. Los métodos que incluye son:

A menos que se diga lo contrario estos métodos lanzan una excepción IOException si detectan algún error en el flujo de salida.

6.3 Salida con formato: la clase PrintStream

La clase PrintStream proporciona utilidades para dar formato a la salida. Tiene dos métodos print y println que están sobrecargados para los tipos primitivos, objetos, cadenas y arrays de caracteres. La diferencia entre ambos métodos está en que println añade un carácter de nueva línea. Además el método println además puede llamarse sin argumentos, produciendo una nueva línea.

System.out es una referencia a PrintStream. PrintStream proporciona dos constructores, ambos con un primer argumento de tipo OutputStream cuya diferencia está en un segundo argumento booleano que indica si debe vaciar el flujo con cada carácter de nueva línea.

6.4 Entrada y Salida de Tipos Primitivos: las clases DataInputStream y DataOutputStream

Aunque leer y escribir bytes es útil, a menudo es necesario transmitir datos de tipos primitivos dentro de un flujo. Las clases DataInputStream y DataOutputStream proporcionan métodos para la lectura y escritura de tipos primitivos de un modo independiente de la máquina. Así proporcionan los siguientes pares de métodos lectura/escritura:readBoolean y writeBoolean, readChar y writeChar, readByte y writeByte, readUnsignedByte y writeUnsignedByte, readShort y writeShort, readUnsignedShort y writeUnsignedShort, readInt y writeInt, readLong y writeLong, readFloat y writeFloat, readDouble y writeDouble, readUTF y writeUTF (UTF es Unicode Transmition Format, que se utiliza para transmitir los caracteres de un String de 16 bits codificada en 8 bits.)

Además de estos métodos la clase DataInputStream proporciona varios métodos más:

La clase DataOutputStream proporciona también un conjunto de métodos aparte de los dedicados a escribir los tipos primitivos. Proporciona signaturas equivalentes a los tres métodos write de la clase OutputStream y además los siguientes métodos:

El siguiente ejemplo implementa un acumulador, que se incrementa en las cantidades que lee del teclado. Se utiliza el método readLine para leer hasta el final de la línea y almacenarlo en un Strint, que luego es tranformado en un entero utilizando el método parseInt de la clase Integer.

import java.io.*;
class Acumulador {
  public static void main( String[ ] args )
    throws IOException
  {
     DataInputStream input = new DataInputStream( System.in );
     String bufferIn;
     int valor, total = 0;
     System.out.print("\n["+total+"] ");
     while((bufferIn = input.readLine()) != null){
        valor = Integer.parseInt( bufferIn );
        total += valor;
        System.out.print("\n["+total+"] ");
     }
     System.out.println("\n["+total+"]\n");
  }
}

6.5 Entrada y Salida con Ficheros: las clases FileInputStream y FileOutputStream

Gran parte de la entrada y salida de los programas se realiza basándose en ficheros y para ello java.io aporta dos clases. Una para ficheros de entrada, FileInputStream, y otra para ficheros de salida, FileOutputStream. La diferencia entre ficheros de entrada y de salida es fundamental, pues situaciones como intentar leer de un fichero de salida o viceversa pueden generar en errores. FileOutputStream crea un fichero, salvo que exista y sea de sólo lectura.

Estas clases aportan tres constructores cada una, dependiendo de la información aportada para identificar el fichero a abrir:

El siguiente programa ejemplo maneja dos ficheros, uno de entrada y otro de salida, de modo que copia uno en otro. Para leer y escribir en ellos se utilizan los métodos de la clase InputStream y OutputStream, ya que las clases FileInputStream y FileOutputStream son extensiones de las de entrada y salida básica.

import java.io.*;
 
class FileIO{
public static void main(String [] args)
   throws FileNotFoundException, IOException{
 
   FileInputStream in = new FileInputStream("entrada.txt");
   FileOutputStream out = new FileOutputStream("salida.txt");
 
   int n=0,c;
   System.out.print ("\nCopiando ...");
   while( (c = in.read()) != -1){
      out.write(c);
      n++;
   }
   in.close();
   out.close();
   System.out.print ("\nSe han copiado "+n+" caracteres\n");
}}

6.6 Flujos E/S Estándar

El programador puede utilizar para la E/S los tres flujos del sistema que están disponibles como campos estáticos de la clase System: System.in, System.out y System.err:

Normalmente la entrada estándar está asociada al teclado, y tanto la salida estándar como la salida de error estándar están asociadas al monitor, pero esto se puede cambiar, redirigiendolas a ficheros. Tanto out como err son de tipo PrintStream, por tanto los mensajes de salida normal, como los de error, son mostrados utilizando los métodos print de la clase PrintStream.

Por ejemplo, se puede ejecutar el programa Acumulador anteriormente descrito redirigiendo las entradas y salidas a ficheros de la manera siguiente (versión Unix):

java Acumulador <en.txt >sa.txt 2>er.txt

Siendo el contenido de los ficheros

y estando el fichero er.txt vacio por no haberse producido error alguno.

6.7 Bibliografía


Jesús Vegas
Dpto. Informática
Universidad de Valladolid
jvegas@infor.uva.es