sábado, 14 de julio de 2012

4.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 comunicación en Java, utilizando el paquete ya implementado java.io  cuyo fin es guardar y tomar la información 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 métodos ya establecidos para captar la información de los flujos de entrada y de salida por los dispositivos estándar.
En el caso de los flujos de entrada tiene System.in, el cual suele recibir los datos de teclado, utilizando el método read() para leer los caracteres.
Para los flujos de salida se utiliza System.out y los datos se envían a pantalla, utilizando el método print() o println() cuya diferencia es que con el print la información se manda tal cual al buffer de salida, sin saltar de línea, pero debemos utilizar el método flush() para saltar de línea, en cambio con el println se manda el buffer de salida directamente a la pantalla y se salta de línea.
Existe un flujo de datos para los errores y éste es el System.err, el cual envía la salida también 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 información de entrada es asociando al flujo estandar de entrada la creación de un objeto de InputStreamReader, el cual es utilizado a su vez para la creación 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 leídos.
Al utilizar el objeto de la clase BufferedReader tenemos el método readLine() el cual lee un conjunto de bytes del buffer de entrada hasta detectar el fin de línea.
La manera en la que Java saca la información a salida es utilizando la clase PrintWriter tomando el objeto de salida System.out, para crear el objeto de la clase PrintWriter. Los métodos 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 más comunes son:
  •  FileInputStream(String pathname)
  •  FileInputStream(File file)
  •  FileOutputStream(String pathname)
  •  FileOutputStream(File file)
Una vez que un stream de entrada ha sido construido, pueden llamarse métodos para leer un simple byte, o una porción de un arreglo de bytes.
A continuación 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 información tal como enteros o cadenas de caracteres (int o String), etc.
Java cuenta con manejo de streams de alto nivel. Los más comunes son:
  •  DataInputStream(InputStream instream)
  •  DataOutputStream(OutputStream outstream)
Un ejemplo de cómo grabar en un archivo utilizando un DataOutputStream sería:
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 pequeño 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 sería:
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 aplicación y ver lo que hacen, para que percibas como es que se genera el archivo y luego lo leas desde la otra aplicación.
Otros manejadores de streams de alto nivel:
BufferedInputStream y BufferedOutputStream, manejan internamente un buffer de manera que los bytes puedan ser escritos y leídos 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 carácter. 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:
  •  FileReader(String pathname)
    •  FileReader(File file)
Algunos métodos para lectura que provee la superclase Reader son:
int read() throws IOException.
Regresa el siguiente valor entero del carácter (16 bits: 
0 to 65535), -1 si no hay más caracteres.
int read(char[] cbuf) throws IOException.
Llena el arreglo con los caracters leidos, regresa el número de caracteres que se leyeron.
Un ejemplo de writer de bajo nivel es el FileWriter:
  •  FileWriter(String pathname)
    •  FileWriter(File file)
BufferedReader y BufferedWriter. Estas clases tienen buffers internos de manera que los datos pueden ser leídos 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 conversión de los bytes y un sistema (charset). Ejemplo:
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
PrintWriterSimilar a PrintStream, pero escribe caracteres en vez de bytes. Ejemplo.
PrintWriter stdErr = new PrintWriter(System.out,true);
A continuación 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 número");
 int numero = Integer.parseInt(ent.readLine());
 if (numero < 0) {
  salErr.println("Error numero negativo");
 }
 else {
  sal.println("" + (Math.pow(numero,2)));
 }
}
}
La aplicación se pudiera visualizar como los ejemplos siguientes:
En este ejemplo observamos como se utiliza el objeto ent para pedir el dato, el objeto sal es utilizado para desplegar la salida correcta y el objeto salErr es utilizado para desplegar la salida con errores.
Es una convención de Java el utilizar la salida con errores salErr para poder tener la diferencia entre la salida normal y la salida con errores, de esta manera, si tuvieramos aplicaciones que se entrelazan para que la salida de una aplicación sea la entrada de otra, no entremezclamos la salida del proceso normal con la salida de los errores o desplegados.
Si te das cuenta la salida con errores salErr fue utilizada para pedir el número, ya que en la pantalla seria lo mismo utilizar sal o salErr, pero si redireccionamos sal, y salErr, entonces salErr pudiera seguir siendo la pantalla para ver los que me pide la aplicación  y lo que se despliega con error, pero sal pudiera ser redireccionado a un archivo o a otra aplicación y nosotros como usuarios no veríamos esa salida.
Las clases de FileReader y FileWriter proveen acceso a los datos de disco. El FileReader extiende del InputStreamReader y el siguiente es uno de sus constructores:
 FileReader(String fileName)

Un constructor para FileWriter:
FileWriter(String fileName)
Se usan archivos para almacenar datos que se quieren conservar después de que el programa que los generó ha terminado su ejecución.
Los archivos se guardan en dispositivos de almacenamiento secundario (discos ó cintas magnéticas).
El uso de archivos es una de las capacidades más importantes que debe tener un lenguaje para apoyar aplicaciones comerciales que suelen manejar grandes cantidades de datos y que requieren que dichos datos sean persistentes.
En Java la entrada/salida (I/O) de datos se maneja a través de streams.
La entrada y salida puede ser de archivos, y de teclado y pantalla. Se usan streams en ambos casos.
Un stream es un objeto que puede:
  •  Tomar datos de una fuente (archivo o teclado) y llevarlos a un programa.
    •  Tomar datos del programa y entregarlos a un destino (archivo o pantalla)
Ya se mencionó que para leer/escribir de consola y de archivos se usan streams.
Para declarar la variable, crear el objeto y abrir el archivo utilizamos:
  •  BufferedReader fileIn = new BufferedReader(new FileReader(nomArch));
    •  PrintWriter fileOut = new PrintWriter(new FileWriter(nomArch));
    •  PrintWriter fileApnd = new PrintWriter(new FileWriter(nomArch, true));
Nota que en un solo paso estás haciendo las 3 acciones mencionadas.
Observa también que los objetos que estás creando son exactamente de las mismas clases que los que usamos para leer/escribir de consola.
Dado que para escribir en el archivo se utiliza la clase PrintWriter, se escribe en el archivo de la misma forma que se hace a pantalla:
  fileOut.println(unString);
  fileOut.print(unString);
  fileOut.flush();
Estas funciones trabajan de la forma que ya conocemos.
Dado que para leer del archivo se utiliza la clase BufferedReader, se lee del archivo de la misma forma que se hace del teclado:
  fileIn.readLine();
readLine() lee una línea del stream de entrada y regresa esa línea.
A continuación encontrarás un ejemplo que te muestra como crear un archivo de texto a partir de la consola:
import java.io.*;
public class AplicacionFiles1 {
public static void main(String[] args) throws IOException {
 BufferedReader ent = new BufferedReader(new InputStreamReader(System.in));
 PrintWriter sal = new  PrintWriter(new FileWriter("datos.txt"));
 PrintWriter salErr = new  PrintWriter(System.err, true);
 salErr.println("Da el número");
 int numero = Integer.parseInt(ent.readLine());
 while (numero > 0) {
  sal.println("" + numero);
  salErr.println("Da el número");
  numero = Integer.parseInt(ent.readLine());
 }
 salErr.println("Fin");
 sal.close();
}
}
La ejecución de esta aplicación pudiera ser:
El archivo generado se vería así:
Ya que fue generado con extensión txt se abre desde el NotePad.
Una manera de leer el archivo es tomando el BufferedReader con un objeto de FileReader y entonces pasarle como parámetro el nombre del archivo, y en un ciclo leer hasta que no encontremos ningún valor mas a leer, como el ejemplo:
import java.io.*;
public class AplicacionFiles2 {
public static void main(String[] args) throws IOException {
 BufferedReader ent = new BufferedReader(new FileReader("datos.txt"));
 PrintWriter sal = new  PrintWriter(System.out,true);
 PrintWriter salErr = new  PrintWriter(System.err, true);
 String linea = ent.readLine();
 int numero;
 while (linea != null) {
  numero = Integer.parseInt(linea);
  sal.println("" + numero);
  linea = ent.readLine();
 }
 salErr.println("Fin");
 ent.close();
}
}

No hay comentarios:

Publicar un comentario