En esta sesión practicaremos conceptos algo más avanzados de Entrada/Salida, como la lectura de tokens desde ficheros, y la lectura de objetos complejos.
1. En este primer ejercicio practicaremos la lectura de tokens de un fichero, y su almacenamiento para realizar alguna operación.
; Comentario de cabecera m n A11 A12 A13... A21 A22 A23... ...
donde m son las filas, n las columnas, y después aparece
la matriz puesta por filas, con un espacio en blanco entre cada elemento.
El ejercicio leerá la matriz (utilizando un StreamTokenizer sobre el
fichero), construirá una matriz (array) con los datos leídos, después
elevará al cuadrado cada componente, y volcará el resultado en un fichero
de salida.
public Ej6() { StreamTokenizer st = new StreamTokenizer(new FileReader("matriz.txt")); }
public Ej6() { StreamTokenizer st = new StreamTokenizer(new FileReader("matriz.txt")); st.commentChar(';'); }
public Ej6() { StreamTokenizer st = new StreamTokenizer(new FileReader("matriz.txt")); st.commentChar(';'); int filas, columnas; st.nextToken(); filas = (int)(st.nval); // Filas st.nextToken(); columnas = (int)(st.nval); // Columnas }
NOTA: asumimos que el fichero va a tener un formato correcto, y no
tenemos que controlar que haya elementos no deseados por enmedio.
¿Qué se habría leído en primer lugar si no hubiésemos identificado
la primera línea como comentario? ¿Dónde podríamos haber consultado
ese valor leído?
public Ej6() { ... int[][] matriz = new int[filas][columnas]; int t; for (int i = 0; i < filas; i++) for (int j = 0; j < columnas; j++) { t = st.nextToken(); if (t != StreamTokenizer.TT_EOF) { matriz[i][j] = (int)(st.nval); } } }
public Ej6() { ... for (int i = 0; i < filas; i++) for (int j = 0; j < columnas; j++) { matriz[i][j] = (int)(Math.pow(matriz[i][j], 2)); } // Volcamos la salida a fichero PrintWriter pw = new PrintWriter (new FileWriter("matrizSal.txt")); pw.println ("; Matriz resultado"); pw.println ("" + filas + " " + columnas); for (int i = 0; i < filas; i++) { for (int j = 0; j < columnas; j++) { pw.print("" + matriz[i][j] + " "); } pw.println(); } pw.close(); }
; Matriz resultado 3 3 1 4 9 16 25 36 49 64 81
Prueba también a pasarle este mismo fichero como entrada al programa, y que genere otro fichero de salida diferente.
hola 1 pepe 2 otra 53 adios 877Construye varios ficheros ejemplo de entrada, e implementa la clase LeeFicheroAlternado, que lea estos ficheros mediante un StreamTokenizer. La clase deberá lanzar una excepción del tipo adecuado, indicando error de sintaxis, si el fichero no tiene la estructura adecuada. En caso de ser correcto, mostrará su contenido en pantalla.
2. En este segundo ejercicio practicaremos cómo utilizar los
ficheros para almacenar y leer objetos complejos. Hasta ahora sólo hemos
trabajado con enteros o cadenas, y para leerlos basta con leer un stream de
bytes, o utilizar un tokenizer y procesar el fichero de la forma que nos
convenga.
Imaginemos que trabajamos con un objeto complejo que encapsula diferentes tipos
de datos (enteros, cadenas, vectores, etc). A la hora de guardar este elemento
en fichero, se nos plantea el problema de cómo representar su información para
volcarla. De la misma forma, a la hora de leerlo, también debemos saber cómo
extraer y recomponer la información del objeto. Veremos que hay clases Java que
hacen todo este trabajo mucho más sencillo.
public Ej7() { ObjetoFichero of = new ObjetoFichero ("cad1", 1, 1.5); of.addCadena("cad2"); of.addCadena("cad3"); ObjetoFichero of2 = new ObjetoFichero ("cad1b", 2, 2.5); of2.addCadena("cad2b"); of2.addCadena("cad3b"); }El vector de elementos de cada objeto tiene 3 cadenas de texto, más aparte los valores que le hemos dado a los otros campos de la clase.
public Ej7() { ... ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ficheroObj.dat")); oos.writeObject(of); oos.writeObject(of2); oos.close(); }
Observa lo sencillo que resulta guardar objetos complejos de esta forma. La única condición que deben cumplir es que deben ser Serializables.
Finalmente, leeremos los objetos guardados del fichero que hemos generado, y sacaremos por pantalla los valores de sus campos. Para leer los ficheros utilizaremos el análogo a la clase anterior, es decir, un objeto de tipo ObjectInputStream, que permite leer objetos complejos enteros (siempre que sean serializables), desde flujos de entrada. Sólo hay que utilizar su método readObject, que extraerá cada objeto de ese tipo que tengamos en el flujo.
public Ej7() { ... ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ficheroObj.dat")); ObjetoFichero ofLeido1 = (ObjetoFichero)(ois.readObject()); ObjetoFichero ofLeido2 = (ObjetoFichero)(ois.readObject()); }
Es importante hacer notar que el método readObject devuelve un
objeto de tipo Object, que luego nosotros debemos convertir (con un
cast) al tipo de datos que necesitemos.
Para sacar el valor de los campos por pantalla, recordemos que cada objeto
de tipo ObjetoFichero tiene un método llamado imprimeObj
que devuelve una cadena que representa su contenido. Así que basta con
imprimir esa cadena:
public Ej7() { ... System.out.println(ofLeido1.imprimeObj()); System.out.println(ofLeido2.imprimeObj()); }
PARA ENTREGAR