Java__-_FLUJOS_DE_ENTRADA_Y_SALIDA.pdf

(92 KB) Pobierz
FLUJOS DE ENTRADA Y SALIDA
Los programas nos entregan información producto de las funciones que se le han definido,
esta debe provenir de alguna fuente de datos (discos, CD-RW, memoria) y seguramente
necesitaremos enviar esta información o datos hacia otro lugar para mostrarla o
almacenarla. Estos son los flujos (en inglés stream) de información.
"Un flujo es una ruta seguida por los datos de un programa. Un flujo de entrada envía datos
desde una fuente a un programa, y un flujo de salida envía datos desde un programa hacia
un destino".
En JAVA, un flujo es un objeto que sirve de intermediario entre el origen y el destino de los
datos. Esto tiene la ventaja que el programa leerá y escribirá en el flujo de información sin
importar el origen o el destino (la pantalla, un archivo, la memoria, Internet, etc.). Además,
tampoco va a tener relevancia el tipo de dato que se encuentra en este objeto. Por otro lado,
esto significa un nuevo nivel de abstracción pues al programa ya no le importa saber nada
acerca del dispositivo del cual vienen o al cual van los datos.
Así, para leer información el programa tiene que abrir un flujo (objeto), de la misma manera
que tiene que hacerlo para escribirla o enviarla. Para ello JAVA contiene una serie de clases
que son parte del paquete java.io. Un programa que use flujos de entrada/salida (E/S)
deberá importar el paquete:
import java.io.*.
Existen dos tipos de flujos: los de Entrada, que sirven para leer datos, y los de Salida, que se
usan para guardar datos. En ambos casos, los flujos pueden ser flujos de bytes o flujos de
caracteres.
Los Flujos de Bytes, se utilizan para manejar bytes, enteros u otros tipos simples en el flujo,
con valores que van desde 0 a 255.
Los Flujos de Caracteres, manejan archivos de texto u otras fuentes de texto. Cualquier
clase de datos que comprenda texto debería utilizar este tipo de flujos.
1. Flujos de Entrada
La clase
InputStream
es la que se encarga de establecer el flujo de bytes de entrada de
información. Es una clase abstracta que es superclase de todas las subclases que
representan este flujo. En este sentido, el método más importante es
read(),
que son varios
métodos sobrecargados que leen bytes ya sea individualmente o en conjunto.
Por otro lado, tenemos la superclase
Reader,
que al igual que la superclase InputStream lee
desde el origen, pero sus subclases leen caracteres, es decir,
char
en vez de
byte.
2. Flujos de Salida
La clase
OutputStream
es la superclase de todas las clases que representan un flujo que se
encarga de escribir
bytes
un destino. Su método más importante es
write()
También, existen otra superclase que en vez de escribir o enviar bytes escribe caracteres:
Writer.
3. Flujo E/S Estándar
El paquete
java.lang
proporciona, por intermedio de la clase
System
tres flujos que son
abiertos una vez que el programa se carga en memoria para el uso de la salida estándar,
normalmente el monitor:
System.in,
que es una subclase de la clase
InputStream
y que hace referencia a la entrada
estándar del sistema, normalmente el teclado. Se usa para leer datos introducidos por el
usuario.
System.out,
que es subclase de
PrintStream
que a su vez es subclase de
OutputStream
y
que hace referencia a la salida estándar. Se utiliza para mostrar datos al usuario.
System.err,
que es subclase de
PrintStream
que a su vez es subclase de
OutputStream
y
que hace referencia a la salida estándar. Se utiliza para mostrar mensajes de error al
usuario.
4. Flujo E/S desde y hacia Archivos
Una gran parte de la vida de una aplicación en memoria, se dedicará a trabajar con archivos
o archivos, para intercambiar datos entre distintos dispositivos de almacenamiento, que se
utilizan refiriéndolos con una ruta de directorio y un nombre.
Las operaciones básicas que se realizan con un archivo son: abrir el archivo, leer datos o
escribirlos y cerrar el archivo.
a. Flujos de Bytes.
Esta es la manera básica de leer o escribir sobre un archivo. Para leer bytes se debe abrir
un flujo de entrada hacia el origen, y para escribir sobre él se abre un flujo de salida hacia el
destino.
Para crear el flujo de entrada del archivo se usa un objeto de la clase
FileInputStream,
a
cuyo constructor se le pasa como argumento una cadena que contiene la ruta del archivo y
su nombre, con su extensión, o sólo el nombre del archivo si se encuentra en el directorio
actual de trabajo. Por ejemplo:
FileInputStream archivo = new FileInputStream("Ruta\Nombre.ext");
Una vez creado el flujo, se podrán leer bytes desde el flujo usando el método
read(),
el cual
devuelve un valor entero que contiene la representación del próximo byte en el flujo y
devuelve -1 cuando llega al final del flujo.
/* Lee los bytes desde un archivo */
import java.io.*;
public class EjemFile {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
String archivo = "";
int enteroByte = 0;
System.out.print("Ingrese el nombre del archivo: ");
archivo = s.nextLine();
try{
FileInputStream f = new FileInputStream(archivo);
enteroByte = f.read();
while (enteroByte != -1){
System.out.print((char)enteroByte);
enteroByte = f.read();
}
f.close();
}
catch (IOException e){
System.out.println(e);
}
}
}
Este programa lee bytes desde un archivo existente. La ruta y/o nombre del archivo se
ingresa por teclado. Se debe considerar la forma como cada sistema operativo maneja el
acceso a sus directorios; así si se trabaja con sistemas UNIX o Linux se usa la barra
inclinada normal (/) para separar los directorios, en cambio, es sistemas DOS y Windows se
usa la barra invertida (\), pero como representa la secuencia de escape se debe usar así: '\\'.
En este programa, se crea el flujo, se lee el archivo byte a byte y se imprimen hasta el fin del
archivo con el que se está trabajando.
Sin embargo, en este caso, estamos leyendo el archivo byte a byte. Sería una mejor
solución, traer a memoria una mayor cantidad de información para trabajar con ella en vez
de ir leyendo cada vez desde el archivo, mejorando así la velocidad de ejecución de la
aplicación. Esto se hace a través de un buffer, "...un lugar donde se pueden guardar datos
antes de ser utilizados por un programa...".
En el caso del flujo de entrada, el buffer se llena con datos que no se han utilizado aún. Y
cuando el programa los requiera los encontrará en el buffer antes de ir a buscarlo a la fuente
origen (archivo físico). La clase con la que se logra esto es:
BufferedInputStream,
que crea
un flujo de entrada almacenado en un buffer reservado para un objeto
InputStream
que se
le pasa como argumento y que, en este caso particular, será un objeto
FileInputStream.
Para leer datos desde la entrada del buffer se usa el método
read(),
sin argumentos, el cual
devuelve un entero de de valor 0 a 255 que representa el byte leído. Si llega al final del flujo
y no quedan datos devuelve un -1. Para ejemplificar, tomaremos el mismo ejemplo anterior y
le agregaremos una línea y modificaremos otra:
...
try{
FileInputStream f = new FileInputStream(archivo);
BufferedInputStream buffer = new BufferedInputStream(f);
enteroByte = buffer.read();
...
En el caso de los flujos de salida, este se crea con la clase
FileOutputStream,
a la cual se
le pasa como argumento la ruta y el nombre del archivo. Si el archivo ya existe, se borrará
su contenido. En caso de no existir se creará, pero si existe, y se quiere agregar su
contenido al final del archivo existe el constructor
FileOutputStream (String rutaArchivo,
[true/false]),
que permite esta operación cuando se pasa
true
como el argumento booleano.
Es el método
write(Entero)
el que permite escribir valores enteros y bytes en el flujo. En este
caso, es necesario cerrar expresamente el flujo hacia el archivo mediante el uso del método
close().
Como ejemplo, el siguiente programa toma la entrada por teclado y la envía a un archivo de
texto (datos.txt). Si el archivo no existe, lo creará, y si ya existe lo sobreescribirá. La entrada
es por un flujo estándar la cual termina al presionar la tecla “Enter” (10):
import java.io.*;
public class EjemFileOut {
public static void main(String[] args) {
int letra;
System.out.print("Ingrese un texto: ");
try{
FileOutputStream f = new FileOutputStream("datos.txt");
do{
letra = System.in.read();
f.write(letra);
}while(letra != 10);
f.close();
}
catch (IOException e){
System.out.println(e);
}
}
}
Para hacer lo anterior, pero sin perder el contenido original del archivo si este ya existe, si no
que agregar la final los bytes que se escriben en el flujo, se escribe el programa
exactamente igual, pero se utiliza el constructor que permite agregar los enteros a partir del
final del archivo, para lo cual se reemplaza la siguiente línea:
FileOutputStream f = new FileOutputStream("datos.txt",
true);
También se puede establecer un buffer para los flujos de salida a través de la clase
BufferOutputStream,
donde se pasa como argumento el objeto del flujo de salida
establecido para el archivo a escribir. Se usa el método
write(entero),
para escribir bytes en
el archivo, que representen valores enteros de 0 a 255. Los datos se escriben en el flujo y
sólo se escriben en el archivo cuando el buffer se llena o cuando se llama expresamente al
método
flush().
Zgłoś jeśli naruszono regulamin