You are on page 1of 11

Tema 6. Lectura y escritura Autor: Jos C.

Riquelme
1. Lectura: la clase Scanner 1.1 Definicin Java proporciona en el paquete java.util una clase que se denomina Scanner que nos permitir leer datos desde ficheros de texto o incluso desde teclado. Los objetos de tipo Scanner mediante la invocacin de distintos mtodos permitirn leer datos de cualquier tipo (String, enteros, reales, etc) con diversas posibilidades de separadores. Asimismo permitir leer un fichero de texto lnea a lnea, guardando cada lnea en un objeto de tipo String. Para construir un objeto de tipo Scanner se invocar al constructor de la clase pasndole como argumento un objeto de tipo File (que se encuentra en el paquete java.io). Los objetos de tipo File relacionan un fichero con su nombre y path en el sistema de archivos del ordenador. Un objeto de tipo File se crea mediante un constructor al que se le pasa como argumento una cadena de caracteres con el nombre y el path del fichero que se quiere leer o escribir. Por ejemplo: File f = new File("palabras.txt"); Indica que el fichero palabras.txt se encuentra en el directorio raz de la carpeta que contenga nuestro proyecto Java. Si quisiramos que el fichero estuviera en una carpeta concreta podramos poner un constructor como: File f = new File("c:\Usuarios\pedro\clases\poo\tema6\palabras.txt"); Y en la carpeta del paquete test del proyecto podramos poner: File f = new File(".\src\test\palabras.txt"); Una vez creado el fichero f se puede invocar al constructor de la clase Scanner: Scanner sc = new Scanner(f); Sin embargo lo usual es hacerlo todo en la misma sentencia ya que el objeto de tipo File normalmente no se va a usar: Scanner sc = new Scanner(new File("palabras.txt")); 1.2 Mtodos de la clase Scanner La clase Scanner proporciona un conjunto de mtodos para leer el contenido del fichero de texto que se ha conectado mediante la invocacin del constructor. En la siguiente tabla se exponen algunos de los ms interesantes, como siempre se puede ver la relacin completa en la documentacin de Java: http://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html

Introduccin a la Programacin

void boolean boolean

close()
Closes this scanner.

hasNext()
Returns true if this scanner has another token in its input.

hasNextDouble()
Returns true if the next token in this scanner's input can be interpreted as a double value using the nextDouble() method.

boolean

hasNextInt()
Returns true if the next token in this scanner's input can be interpreted as an int value in the default radix using the nextInt() method.

boolean boolean

hasNextLine()
Returns true if there is another line in the input of this scanner.

hasNextLong()
Returns true if the next token in this scanner's input can be interpreted as a long value in the default radix using the nextLong() method.

String double int

next()
Finds and returns the next complete token from this scanner.

nextDouble()
Scans the next token of the input as a double.

nextInt()
Scans the next token of the input as an int.

String long Scanner

nextLine()
Advances this scanner past the current line and returns the input that was skipped.

nextLong()
Scans the next token of the input as a long.

useDelimiter(String pattern)
Sets this scanner's delimiting pattern to a pattern constructed from the specified String.

Antes de explicar el uso de estos mtodos hay que resear un par de cuestiones: Java permite crear objetos de tipo Scanner invocando distintos constructores, no solo con argumentos de tipo File, sino tambin InputStream, Readable o incluso un String. Estos constructores se mantienen por compatibilidad entre versiones de Java y para permitir lecturas a nivel de byte. El mtodo useDelimiter sobre un objeto Scanner lleva como argumento un objeto de tipo Pattern: http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html que es una representacin como cadena de una expresin regular: http://es.wikipedia.org/wiki/Expresi%C3%B3n_regular En esta asignatura slo lo vamos a usar para indicar separadores entre los datos a leer distintos del espacio en blanco que es el separador por defecto de la clase Scanner. Hay que tener en cuenta que el constructor de Scanner lanza la excepcin de tipo FileNotFoundException, por tanto el mtodo que contenga la invocacin debe lanzar (throws) una excepcin.

6. Lectura y escritura

1.3 Uso de la clase Scanner Como hemos podido observar en la tabla anterior la clase Scanner nos permite leer un fichero de texto de diversas maneras: lnea a lnea, String a String (con algn carcter separador) o incluso leer otros tipos de datos como enteros, reales, etc. Ejemplo 1 Supongamos que queremos leer un fichero de texto lnea a lnea guardando cada lnea en un String:
Scanner sc=new Scanner(new File("palabras.txt")); while (sc.hasNextLine()) { String s=sc.nextLine(); //tratamiento de s, por ejemplo: mostrar(s); } sc.close();

Como podemos ver los mtodos usados son hasNextLine que nos devuelve cierto mientras haya ms lneas por leer y falso cuando el fichero se acaba y nextLine que devuelve la siguiente lnea del fichero en un String. De esta forma si el fichero palabras.txt tuviera este contenido: cinco cinco cinco seis seis siete siete ocho La salida del programa anterior sera una copia del fichero lnea a lnea: cinco cinco cinco seis seis siete siete ocho Ejemplo 2 Supongamos que queremos leer un fichero de texto palabra a palabra separadas por un blanco guardando cada palabra en un String:
Scanner sc=new Scanner(new File("palabras.txt")); while (sc.hasNext()) { String s=sc.next(); //tratamiento de s, por ejemplo: mostrar(s); } sc.close();

Introduccin a la Programacin

Como podemos ver ahora los mtodos usados son hasNext que nos devuelve cierto mientras haya ms palabras por leer y falso cuando el fichero se acaba y next que devuelve la siguiente palabra del fichero en un String. De esta forma si el fichero palabras.txt tuviera este contenido: cinco cinco cinco seis seis siete siete ocho La salida del programa anterior sera: cinco cinco cinco seis seis siete siete ocho

Ejemplo 3 Supongamos que queremos leer un fichero de texto palabra a palabra separadas por una coma guardando cada palabra en un String:
Scanner sc=new Scanner(new File("palabrascomas.txt")).useDelimiter(","); while (sc.hasNext()) { String s=sc.next(); //tratamiento de s, por ejemplo: mostrar(s); } sc.close();

Como podemos ver la diferencia con el anterior ejemplo es el uso del mtodo useDelimiter que permite leer separando por otros caracteres distintos de blanco. Si el fichero palabrascomas.txt tuviera este contenido: cinco,cinco,cinco,seis,seis,siete,siete,ocho La salida del programa anterior sera: cinco cinco cinco seis

6. Lectura y escritura

seis siete siete ocho

Ejemplo 4 Si nuestro fichero contuviera datos de tipo numrico tambin se podran leer con la clase Scanner. Por ejemplo si quisiramos construir una lista de tipo Integer con el contenido del siguiente fichero enteros.txt: 23 24 35 45 36 34 37 Escribiramos el siguiente cdigo: List<Integer> ls = new LinkedList<Integer>(); Scanner sc = new Scanner(new File("numeros.txt")); while (sc.hasNextInt()) { ls.add(sc.nextInt()); } sc.close(); mostrar("los nmeros ledos: ",ls); y la salida sera los nmeros ledos: [23, 24, 35, 45, 36, 34, 37] Si en vez de leer nmeros enteros quisiramos leer nmeros reales sustituiramos los mtodos hasNextInt y nextInt por hasNextDouble y nextDouble. Ejemplo 5 Finalmente la clase Scanner tambin nos permite leer datos desde teclado. Para ello en el constructor pasamos como argumento el objeto System.in, atributo de la clase System para sealar la entrada estndar o teclado. As por ejemplo si quisiramos leer nmeros enteros desde teclado y aadirlos a una lista hasta leer uno negativo, esto es si introducimos por teclado la secuencia:
23 45 65 23 45 67 -6 78 76 45 La salida sera: los nmeros ledos: [23, 45, 65, 23, 45, 67]

El cdigo para esto es:

Introduccin a la Programacin

List<Integer>ls = new LinkedList<Integer>(); Scanner sc = new Scanner(System.in); boolean fin=false; while (sc.hasNext() && !fin) { Integer i= sc.nextInt(); if (i>=0) ls.add(i); else fin=true; } sc.close(); mostrar("los nmeros ledos: ",ls); Si quisiramos leer una secuencia completa, para terminar introduciramos en la consola de entrada Ctrl-Z que es la secuencia que indica fin de lectura. 1.4 Encapsulacin de la lectura Los trozos de cdigo de los ejemplos anteriores no estn situados en ningn mtodo en concreto. Como ya sabemos de temas anteriores, la programacin orientada a objetos debe encapsular las lneas de cdigo de una determinada accin en un mtodo, bien sea un mtodo de una clase, bien sea un mtodo de utilidad. Por tanto, si estamos codificando un mtodo de una clase que necesita realizar una lectura de datos, en el mtodo correspondiente escribiramos las sentencias anteriores. Tambin hay que sealar que en los trozos de cdigo anteriores no se ha incluido el tratamiento de la excepcin que puede generar la construccin del objeto de tipo Scanner. Por tanto, el mtodo donde se incluya la invocacin del constructor de Scanner debe lanzar (throws) o capturar (try/catch) la excepcin FileNotFoundException. Sin embargo, una forma habitual de usar la lectura de datos es encapsularla en un mtodo de utilidad que devuelva un objeto de tipo List con los objetos ledos. Incluso se puede suponer que los objetos siempre sern de tipo String ya que a partir de estos es fcil obtener los tipos numricos habituales mediante la invocacin de los correspondientes constructores. Ejemplo 6 Supongamos que queremos obtener datos de un fichero, sin importarnos el tipo, slo sabemos que estn separados por comas y guardarlos en un List de String. Vamos a construir un mtodo esttico en una clase Utiles de la siguiente forma:
public static List<String> leeFichero(String fileName, String del) { List<String> listaleida= new LinkedList<String>(); try { Scanner sc = new Scanner(new File(fileName)).useDelimiter("\\s*"+del+"\\s*"); while (sc.hasNext()) { listaleida.add(sc.next()); } sc.close(); } catch (FileNotFoundException e) {

6. Lectura y escritura

System.out.println("Fichero no encontrado "+fileName); } return listaleida; }

Algunas cuestiones respecto al cdigo anterior: El mtodo leeFichero devuelve una lista de String independientemente de cuales sean los datos contenidos en el fichero. El mtodo recibe un String con el nombre del fichero a leer y una cadena con el carcter separador. La invocacin al mtodo useDelimiter se hace concatenando el carcter separador con cualquier combinacin de blancos, tabuladores o saltos de lnea delante o detrs. Esa combinacin es indicada por la expresin regular \\s*. La construccin del objeto Scanner est incluida en el cuerpo de una sentencia try/catch para capturar una posible excepcin de fichero no encontrado, que indicar que el fichero o no existe o no est donde se espera.

La invocacin a este mtodo por ejemplo para leer los enteros de este fichero: 23, 24, 35, 45, 36, 34, 37, 56, 45, 67 Sera en una clase TestLectura como sigue: public static void main(String[] args) { List<String> lst = Util.leeFichero("numeros_comas.txt",","); List<Integer> li = new LinkedList<Integer>(); for(String s: lst){ li.add(new Integer(s)); } mostrar("Los nmeros ledos son ",li); } Donde la salida sera
Los nmeros ledos son [23, 24, 35, 45, 36, 34, 37, 56, 45, 67]

Para leer datos separados por blancos podramos usar una sobrecarga del mtodo que no tuviera parmetro delimitador. Su cdigo sera:
public static List<String> leeFichero(String fileName) { List<String> listaleida= new LinkedList<String>(); try {

Introduccin a la Programacin

Scanner sc = new Scanner(new File(fileName)).useDelimiter("\\s+"); while (sc.hasNext()) { listaleida.add(sc.next()); } sc.close(); } catch (FileNotFoundException e) { System.out.println("Fichero no encontrado"); } return listaleida; } Dnde la expresin "\\s+" indica cualquier combinacin de al menos un blanco, un tabulador o un salto de lnea.

Ejemplo 7 Supongamos ahora que se necesita leer un fichero de texto con los valores de los atributos de una clase, de forma que la salida sea una coleccin de objetos creados a partir de esos valores. As si tenemos el fichero personas.txt con el siguiente contenido: Pedro Gmez,11111111A,25 Luisa Espinel,222222222B,24 Pedro Gmez,33333333C,24 Mariana Guerrero,44444444D,28 Vamos a crear un mtodo de utilidad que devuelva un List<Persona> con los cuatro objetos de tipo Persona, que estarn organizados por filas, de forma que cada fila contiene los valores de los atributos de un objeto separados por comas. El mtodo recibir un String con el nombre del fichero y lo leer lnea a lnea. Cada una de estas lneas contiene los valores de los atributos (nombre, DNI y edad) necesarios para construir un objeto Persona. Para separar estos valores se va a usar un mtodo auxiliar que hemos denominado separaElementos para a partir de otro objeto de tipo Scanner segmentar la lnea en una List de String con los atributos. El cdigo es:
public static List<Persona> leePersonas(String nomFich) throws FileNotFoundException{ List <Persona> lp = new LinkedList<Persona>(); Scanner sc=new Scanner(new File(nomFich)); while (sc.hasNextLine()) { String linea = sc.nextLine(); List<String> lisat= separaElementos(linea); Persona p=new PersonaImpl(lisat.get(0),lisat.get(1), new Integer(lisat.get(2))); lp.add(p); } return lp; } public static List<String> separaElementos(String s){ List<String> ls = new LinkedList<String>(); Scanner sc1=new Scanner(s).useDelimiter(","); while (sc1.hasNext()){

6. Lectura y escritura

ls.add(sc1.next()); } return ls; }

Algunas cuestiones respecto al cdigo anterior: La excepcin FileNotFoundException no ha sido tratada mediante un try/catch para no dificultar la lectura del cdigo. Por eso slo se ha propagado (throws) hacia el mtodo invocante. En un cdigo bien hecho debera estar la sentencia try/catch. El mtodo separaElementos no depende de Persona o del tipo que estemos construyendo. Es un mtodo de propsito general que recibe un String s y devuelve un List de String con los trozos de s que se encuentran separados por comas. Como se puede observar el tipo Scanner tambin es capaz de leer de una cadena de caracteres, simplemente ponindola en el lugar del objeto de tipo File en la invocacin al constructor. El constructor de PersonaImpl que estamos invocando recibe dos String (nombre y DNI) y un Integer (edad) que es convertido a Integer en la invocacin al constructor. Tambin debera incluirse el lanzamiento de una excepcin si el nmero de elementos de la lista lisat de atributos n tiene los tres elementos que debera tener si el fichero est bien construido. Esto es, se debe lanzar una excepcin que advierta si el fichero tiene tres valores separados por comas en cada lnea. 2. Escritura: la clase printWriter 2.1 Definicin Java proporciona dos clases bsicas para escribir en un fichero de texto: PrintStream y PrintWriter. Su uso es muy parecido y las diferencias son ms internas que externas para un programador no avanzado. La principal diferencia es que PrintWriter (que es posterior a printStream) no puede ser usado para escribir bytes en crudo ( raw en ingls), y por tanto su uso habitual es para datos que tengan una representacin en texto o dicho de otra manera para tipos que tengan el mtodo toString. Nosotros vamos a usar la clase PrintWriter aunque sus mtodos para escribir objetos en formato carcter son similares a los de PrintStream. Una ventaja de PrintWriter es que sus mtodos no lanzan excepciones, aunque s alguno de sus constructores. El constructor de la clase PrintWriter recibe un objeto de File y puede lanzar la excepcin FileNotFoundException si se produce algn problema para su apertura (normalmente disco lleno o protegido). Por tanto un posible cdigo para la creacin de un objeto PrintWriter es: File file = new File(filename); try { PrintWriter ps = new PrintWriter(file); } catch (FileNotFoundException e) { System.out.println("Fichero no encontrado "+filename); }

10

Introduccin a la Programacin

2.2 Mtodos de la clase PrintWriter Una seleccin de los mtodos que java proporciona a la clase PrintWriter son los de la siguiente tabla. Como siempre se pueden consultar todos en: http://docs.oracle.com/javase/7/docs/api/java/io/PrintWriter.html
void close()
cierra el fichero

void

print(Tipo

t)

Escribe un valor t de tipo Tipo, donde Tipo puede ser char, boolean, char[ ], double, int, String, long, float u Object

void

println()
Salta de lnea

void

println(Tipo t)
Escribe un objeto de tipo boolean, char, char [ ], String, double, float, int, long u Object y despus salta de lnea

2.3 Uso de la clase PrintWriter Como se puede ver de la tabla anterior escribir en un fichero de texto es igual de sencillo que en la consola. Basta con definir un objeto de la clase PrintWriter e invocar con l a los mtodos print o println segn queramos saltar o no de lnea al final del objeto a escribir que pasaremos como argumento. Ejemplo 7 Para escribir en un fichero de texto los valores de una lista de nmeros enteros de manera que estn uno por lnea, el cdigo sera: File file = new File("enteros.txt"); PrintWriter ps = null; try { ps = new PrintWriter(file); for (Integer elem : lista) { ps.println(elem); } ps.close(); } catch (FileNotFoundException e) { System.out.println("Fichero no encontrado "+filename); } 2.4 Encapsulacin de la escritura Igual que se hizo en la lectura de ficheros, podemos hacer un mtodo esttico que reciba una Collection de elementos de cualquier tipo y los escriba en un fichero de texto. Para ello podemos usar los tipos genricos que se estudiaron al final del Tema 3. El cdigo podra ser: public static <T> void escribeFichero(Collection<T> it, String filename) {

6. Lectura y escritura

11

File file = new File(filename); try { PrintWriter ps = new PrintWriter(file); for (T elem : it) { ps.println(elem); } ps.close(); } catch (FileNotFoundException e) { System.out.println("Fichero no encontrado "+filename); } } Si quisiramos que no escribiera cada dato en una nueva lnea sino separar los objetos por un espacio en blanco o por una coma, bastara con sustituir: ps.println(elem); por ps.print(elem+ " "); // ps.print(elem+ ",");

3. Ejercicios 1. Escriba en una clase de utilidad los mtodos que nos permiten encapsular la lectura y escritura de ficheros, aadiendo cuando sea necesario el tratamiento de Excepciones mediante sentencias try/catch. 2. Lea desde teclado una lista de nmeros reales y escrbala en un fichero de texto separados por blancos y en otro cada uno en una lnea. 3. Lea una lista de palabras desde teclado, ordnela alfabticamente y escriba el resultado en un fichero de texto de forma que cada palabra est en una lnea y stas estn numeradas.

You might also like