Codemaker Java

Java Tema 7

Ficheros de datos

En el desarrollo de aplicaciones reales, rara vez trabajamos solo con datos introducidos por el usuario durante la ejecución. Muchas veces es necesario guardar información para usarla más adelante (como resultados, configuraciones, listas de usuarios o registros), o bien leer datos almacenados previamente para procesarlos.

Recuerda que las variables se guardan en memoria RAM, por lo que cuando apagues tu app, los datos se borrarán. Para que la información sea persistente (no se borre), debemos guardarla en el Disco Duro (ficheros).

Los datos viajan entre el disco duro, la memoria RAM y tu programa

Lectura y escritura de datos#

Un flujo o Stream es un canal de comunicación por el que circulan datos desde un lugar hacia otro. Java clasifica los flujos en dos tipos principales:

  • Flujos de entrada (Input): para que Java lea datos que vienen desde otro sitio (como un fichero).
  • Flujos de salida (Output): para que Java escriba datos en otro sitio (como un fichero).

Los flujos de entrada y salida pueden manejar los datos en distintos formatos, lo cual implica que tengamos flujos de bytes y flujos de caracteres:

Tipos de flujos#

Dependiendo de lo que se quiera enviar/recibir, se pueden usar dos tipos de flujos (caminos) en Java:

Tipo de flujoLos datos viajan…Ideal paraPara leerPara escribir
De bytesen binarioImágenes, vídeos, ficheros comprimidos…FileInputStreamFileOutputStream
De caracterescomo textoFicheros .txt, .csv y similaresFileReaderFileWriter

En nuestro caso, utilizaremos por ahora solo Flujos de caracteres.

Mejora del rendimiento con Buffered#

Leer o escribir un fichero carácter a carácter es como llevar agua de un sitio a otro con una cucharilla: funciona, pero es lentísimo. Un Buffered es un cubo: va cargando muchos caracteres en memoria y los lleva de golpe.

Java tiene las dos versiones de cada clase. En la práctica, usaremos siempre la que lleva cubo:

Clase básica (cucharilla)Clase con buffer (cubo)ParaQué ganamos
FileReaderBufferedReaderLeer textoLee una línea entera de golpe con readLine()
FileWriterBufferedWriterEscribir textoEscribe por bloques, y salta de línea con newLine()

Fíjate en que la clase con buffer se construye envolviendo a la básica: new BufferedReader(new FileReader("texto.txt")). La de dentro abre el fichero; la de fuera le pone el cubo.

Excepciones y try con recursos#

Hay errores que no dependen de ti: el fichero no existe, el disco está lleno, no tienes permiso para escribir… Cuando pasa algo así, Java lanza una excepción y, si nadie la atiende, el programa se para. Con los ficheros, Java obliga a decir qué hacer si falla: si no lo haces, el programa ni siquiera compila.

Además, un fichero que se abre hay que cerrarlo al terminar, o puede quedarse a medio escribir.

Las dos cosas se resuelven a la vez con el try con recursos:

Java
try (BufferedReader br = new BufferedReader(new FileReader("texto.txt"))) {
    // aquí se usa el fichero
} catch (IOException e) {
    // aquí, lo que se hace si algo falla
}
// aquí el fichero ya está cerrado, sin escribir nada más
ParteSe lee como…Qué va dentro
try ( … )«Abre esto»El fichero que vas a usar. Java lo cierra solo al terminar, haya error o no
{ … }«E intenta hacer esto con él»El código que lee o escribe
catch (IOException e) { … }«Si algo falla, haz esto en vez de pararte»Lo que queremos hacer si hay error, por ejemplo avisar al usuario. e.getMessage() dice qué ha pasado

Es la forma que usaremos siempre que haya algo que abrir y cerrar: ficheros ahora, y conexiones a una base de datos más adelante.

Ejemplos de código#

Las clases de ficheros están en java.io, así que hay que importarlas arriba del todo:

Java
import java.io.*;

Ejemplo 1: leer un archivo de texto línea por línea con BufferedReader:

Java
try (BufferedReader br = new BufferedReader(new FileReader("texto.txt"))) {
    String linea;
    // readLine() devuelve la siguiente línea, o null cuando ya no quedan más
    while ((linea = br.readLine()) != null) {
        System.out.println(linea);
    }
} catch (IOException e) {
    System.out.println("Error al leer el archivo: " + e.getMessage());
}

Ejemplo 2: escribir varias líneas en un archivo de texto con BufferedWriter:

Java
try (BufferedWriter bw = new BufferedWriter(new FileWriter("salida.txt", false))) {
    bw.write("Primera línea");
    bw.newLine();
    bw.write("Segunda línea");
} catch (IOException e) {
    System.out.println("Error al escribir en el archivo: " + e.getMessage());
}

¡Ojo! Observa la primera línea donde se crea el Buffer, ¿ves un false? Ahí también se podría poner true. Veamos para qué sirve cada uno:

CódigoModoQué pasa con lo que ya había en el fichero
new FileWriter("archivo.txt", true)Append (añadir)Se conserva: lo nuevo se pega al final
new FileWriter("archivo.txt", false)Overwrite (sobrescribir)Se borra: se escribe desde cero

Si el fichero no existe, en los dos casos Java lo crea.

Errores frecuentes#

Mensaje o síntomaQué significaCómo se arregla
unreported exception FileNotFoundException; must be caught or declared to be thrownHas abierto un fichero fuera de un try con recursosÁbrelo dentro de los paréntesis del try, con su catch (IOException e)
cannot find symbol en BufferedReader o FileReaderFalta importar las clasesAñade import java.io.*; arriba del todo
FileNotFoundException: texto.txt (No such file or directory) (en Windows: El sistema no puede encontrar el archivo especificado)Al ejecutar: Java no encuentra el ficheroEl fichero tiene que estar en la carpeta del proyecto (la raíz, no dentro de src), y el nombre debe coincidir exactamente, extensión incluida
El fichero se crea, pero sale vacíoNo se ha cerrado, y lo escrito se quedó en el cubo sin llegar al discoÁbrelo con try con recursos, que lo cierra solo
Se han borrado los datos que ya habíaHas abierto el FileWriter con false (o sin segundo valor), que sobrescribePara añadir al final, usa new FileWriter("archivo.txt", true)