Flujos streams. Tipos.

En Java, cualquier programa que tenga que obtener información de cualquier fuente (fichero, internet, memoria o incluso otro programa) necesita abrir un stream, pero incluso para enviar también necesita de un stream y enviar la información en serie.

Java define la abstracción de stream (flujo) para tratar la comunicación de información entre el programa y el exterior. Entre una fuente y un destino fluye una secuencia de datos. Los flujos actúan como interfaz con el dispositivo o clase asociada.

Tenemos 2 tipos de flujos:

  • 8 bits: orientado a datos binarios. Todas las clases descienden de InputStream y OutputStream y a partir de estas clases se crean otras que se ajustan a los distintos dispositivos de e/s. Los flujos de bytes se usan para manipular datos binarios, legibles sólo por la máquina (por ejemplo un fichero .exe).
  • 16 bits: orienta a caracteres. Las clases Reader y Writer son las que gestionan este tipo de información y están especializadas en el manejo de caracteres Unicode por el tema de la internacionalización. Los flujos de caracteres se usan para manipular datos legibles por humanos (por ejemplo un fichero .txt).

Operativa:

El proceso para operar con los flujos sigue la siguiente secuenciación:

  1. Abrir el stream
  2. Usar el stream (leer, escribir)
  3. Cerrar el stream

 

Clases y subclases. Flujo de Bytes

En la clase InputStream: las fuentes pueden ser array de bytes, objeto string, un fichero, una tubería, una secuencia de otros flujos, otras fuentes como una conexión a Internet.

Los tipos de InputStream se resumen en:

  • ByteArrayInputStream : permite el uso de un espacio de almacenamiento intermedio de memoria.
  • StringBufferInputStream : convierte un String en un InputString
  • FileInputStream : para leer información de un fichero
  • PipedInputStream : implementa el concepto de tuberia.
  • FilterInputStream : proporciona funcionalidad útil a otras clases InputStream
  • SequenceInputStream : convierte dos o más objetos InputStream en un InputStream único.

Clases y subclases. Flujo de Caracteres Unicode.

Hay unas clases que convierten los stream de bytes a stream de caracteres:

Para evitar que cada lectura o escritura acceda directamente al fichero, se puede utilizar un buffer intermedio entre el dispositivo físico y el stream.

Los clases de flujos de caracteres más importantes:

  • Acceso a fichero
    • FileREader
    • FileWriter
  • Acceso a contenido
    • CharArrayReader
    • CharArrayWriter
  • Buferización (utilización de memoria intermedia)
    • BufferedReader
    • BufferedWriter

Es interesante para conseguir lecturas o escrituras constantes, por ejemplo en las grabaciones de CD’s o DVD’s, envío de contenido multimedia por Internet, donde la velocidad no es constante.

Ejemplo lectura y escritura en ficheros:

import java.io.*; 
public class FicheroTextoApp {
    public static void main(String[] args) {
        try{
            //Abro stream, crea el fichero si no existe
            FileWriter fw=new FileWriter("D:\\fichero1.txt");
            //Escribimos en el fichero un String y un caracter
            fw.write("Hol");
            fw.write(97);
            //Cierro el stream
            fw.close(); 
            //Abro el stream, el fichero debe existir
            FileReader fr=new FileReader("D:\\fichero1.txt");
            //Leemos el fichero y lo mostramos por pantalla
            int valor=fr.read();
            while(valor!=-1){
                System.out.print((char)valor);
                valor=fr.read();
            }
            //Cerramos el stream
            fr.close();
        }catch(IOException e){
            System.out.println("Error E/S: "+e);
        }
    }
}

Analizar y averiguar que pasa con el siguiente código, acordarse que el stream es un paso intermedio entre la aplicación y la memoria del dispositivo.

import java.io.*;
//Importamos todas las clases de java.io.
public class FicheroTextoApp {
    public static void main(String[] args) {
        try{
            //Creo los objetos, abro streams
            FileWriter fw=new FileWriter("D:\\fichero1.txt");
            FileReader fr=new FileReader("D:\\fichero1.txt");
            //Escribimos en el fichero un String y un caracter 97 (a)
            fw.write("Hol");
            fw.write(97);
            //Leemos el fichero y lo mostramos por pantalla
            int valor=fr.read();
            while(valor!=-1){
                System.out.print((char)valor);
                valor=fr.read();
            }
            //Cerramos el stream
            fw.close();
            fr.close();
        }catch(IOException e){
            System.out.println("Error E/S: "+e);
        }
    }
}

La solución vendría si utilizamos la función:

...
//Guardamos los cambios del fichero
fw.flush();
...

Pensad en que posición del código anterior habría que colocarlo para que pudiesemos leer bien el fichero.

Una forma más compacta para el código anterior sería:

...
public class FicheroTextoApp {
    public static void main(String[] args) {
        try(FileWriter fw=new FileWriter("D:\\fichero1.txt");
            FileReader fr=new FileReader("D:\\fichero1.txt")){
            ...
            }
        }catch(IOException e){
            System.out.println("Error E/S: "+e);
        }
    }
}

Por último hacemos el código más estructurado mediante métodos:

import java.io.*; 

public class FicheroTextoApp {
    public static void main(String[] args) {
        try(FileReader fr=new FileReader("D:\\fichero1.txt");
            FileWriter fw=new FileWriter("D:\\fichero1.txt")){
            escribeFichero(fw);
            //Guardamos los cambios del fichero
            fw.flush();
            leeFichero(fr);
            fw.close();
            fr.close();
        }catch(IOException e){
            System.out.println("Error E/S: "+e);
        }
    }
 
    public static void escribeFichero(FileWriter fw) throws IOException{
        //Escribimos en el fichero
        fw.write("Hola");
    }
 
    public static void leeFichero(FileReader fr) throws IOException{
        //Leemos el fichero y lo mostramos por pantalla
        int valor=fr.read();
        while(valor!=-1){
            System.out.print((char)valor);
            valor=fr.read();
        }
    }
}

Ejemplos de ficheros binarios:

import java.io.*;
public class FicherosBinariosApp {
    public static void main(String[] args) {
        try(FileOutputStream fos=new FileOutputStream("D:\\fichero_bin.ddr")){
            String texto="Esto es una prueba para ficheros binariosssss";
            // Podemos usar un numero que corresponderá a la tabla ASCII o un array de bytes
            //Copiamos el texto en un array de bytes
            byte codigos[]=texto.getBytes();
            fos.write(codigos);
        }catch(IOException e){
            ....
        }
    }
}

Abrir el fichero “fichero_bin.ddr” y mirar el resultado.

Si queremos leer un fichero binario:

import java.io.*;
public class FicherosBinariosApp {
    public static void main(String[] args) {
        try(FileInputStream fis=new FileInputStream("D:\\fichero_bin.ddr")){
            int valor=fis.read();
            while(valor!=-1){
                System.out.print((char)valor);
                valor=fis.read();
            }
        }catch(IOException e){
            ...
        }
    }
}

Cambiar el fichero: “fichero_bin.drr” por alguna imagen pequeña en cualquier formato (gif, png, jpg, …) y mirar lo que aparece en pantalla.