UP | HOME

Lectura/Escritura de ficheros de texto

Table of Contents

Leer ficheros de texto

Se pueden leer ficheros de texto en Java de diversas maneras que podemos dividir en dos grupos:

  • Lectura por medio de Stream usando la clase de utilidad Files.
  • Lectura por medios tradicionales con BufferedReader.

a) Lectura usando la clase Files: cargar todo el contenido en un String:

La manera más sencilla de leer el contenido de un fichero de texto sería cargarlo en su totalidad en un String, usando la clase de utilidad Files y el método estático readString. Esto solo se puede hacer si el fichero es pequeño porque se carga en su totalidad en memoria:

var path = Path.of("/usr/share/dict/words.txt");
String contenido = Files.readString(path);

Características y aclaraciones:

  • Solo se puede usar si el fichero es pequeño porque se carga todo el contenido en memoria así que existe el riesgo de colapso de la memoria.
  • La clase de utilidad Files se encarga de abrir, leer y cerrar el fichero automáticamente.

b) Lectura usando la clase Files: cargar todo el contenido en una lista de String:

Otra opción sería usar el método readAllLines para obtener una lista de las líneas del fichero y, así, obtener una variante en la que conoces la estructura de las líneas del fichero:

var path = Path.of("/usr/share/dict/words.txt");
List<String> lineas = Files.readAllLines(path);

Características y aclaraciones:

  • Solo se puede usar si el fichero es pequeño porque se carga todo el contenido en memoria así que existe el riesgo de colapso de la memoria.
  • La clase de utilidad Files se encarga de abrir, leer y cerrar el fichero automáticamente.

c) Lectura usando la clase Files: leer línea a línea

Si necesitas leer ficheros de texto muy grandes para evitar que la memoria colapse puedes usar esta opción en la que se usan los Stream de Java. El método lines devuelve un Stream del que podemos ir recuperando las líneas de una en una. Aquí estamos obligados a cerrar el flujo para liberar el fichero asociado:

var path = Path.of("/usr/share/dict/words.txt");
Stream<String> lineas = Files.lines(path);
// ...

lineas.close();

Características y aclaraciones:

  • Es necesario cerrar de forma explícita el fichero para liberar los recursos.
  • Se puede usar la estructura try-with-resources para que el cierre del fichero se haga automáticamente al salir del bloque try.

Te muestro un ejemplo completo en el que se leen las líneas de un fichero y se imprimen por pantalla:

import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;

public class Main {
    public static void main(String args[]) {
        if (args.length != 1) {
            System.err.println("Uso: java Main.java <ruta del fichero de texto>");
            System.exit(-1);
        }

        var path = Path.of(args[0]);
        if (Files.notExists(path) || !Files.isRegularFile(path)) {
            System.err.println(path + " no existe o no es un fichero de texto");
            System.exit(-1);
        }

        // En estas dos líneas se hace todo lo que se pide:
        try (var lineas = Files.lines(path)) {
            lineas.forEach(System.out::println);
        } catch (IOException e) {
            System.err.println("Se lanzó una excepción: " + e);
        }
    }
}

d) Lectura usando un BufferedReader: forma clásica

Se usa la clase de utilidad Files para obtener un objeto BufferedReader:

  var path = Path.of("/usr/share/dict/words.txt");
  try (BufferedReader reader = Files.newBufferedReader(path)) {
    String linea;
    while ((linea = reader.readLine()) != null) {
        // Procesar línea a línea de forma imperativa
    }
}

Características y aclaraciones:

  • Se usa un búfer intermedio de Java que limita el número de llamadas al sistema.
  • Es necesario cerrar de forma explícita el fichero para liberar los recursos o usar un try-with-resources.

Escribir ficheros de texto

De nuevo, igual que en el caso de la lectura, se pueden usar, básicamente dos opciones generales:

  • Usar la clase de utilidad Files.
  • Usar flujos tradicionales con PrintWriter o usar BufferedWriter.

Ten en cuenta que para escribir en un fichero hay que abrirlo (open). A continuación se escriben los datos (write) pero esto no tiene por qué hacerse en disco, se suele hacer en una memoria especial llamada búfer. Así pues, cuando se acabe de trabajar con el fichero hay que cerrarlo (close) lo que llevará a que se vuelquen los datos que pueda haber en el búfer a disco (flush).

Entendido esto, escribir en un fichero, se puede resumir en hacer las siguiente acciones:

  1. Abrir un descriptor de fichero o flujo a un fichero (open).
  2. Escribir (write)
  3. Volcar el contenido al fichero (flush) y cerrar el fichero (close).

El método close garantiza el volcado físico en disco y no haría falta hacer un flush explícito.

Por último, al abrir (open) un fichero para escribir en él se puede hacer en dos modos:

  • Modo sobreescritura: el contenido del fichero será sustituido por lo que se escriba, es decir, el contenido anterior será eliminado y sustituido por el nuevo.
  • Modo añadir: el nuevo contenido se escribe al final del fichero, manteniendo el contenido ya existente sin borrarlo.

a) Escribir usando clase de utilidad Files: escribir el contenido de un String

Escribe el contenido indicado sustituyendo el contenido actual del fichero (modo sobreescritura):

var ruta = Path.of("archivo.txt");
String contenido = "Cualquier cadena de texto";
Files.writeString(ruta, contenido);

Por defecto:

  • Si el fichero ruta existe se sobreescribirá, eliminando todo el contenido que pudiera tener.
  • Si el fichero ruta no existe se crea con el contenido del String.

Si quieres añadir al final del archivo hay que especificar, como en el ejemplo de abajo:

  • Tercer parámetro: qué hacer si el fichero no existe.
  • Cuarto parámetro: la opción para añadir al final del archivo.
var ruta = Path.of("archivo.txt");
String contenido = "Cualquier cadena de texto";

// CREATE: si no existe el fichero lo crea
// APPEND: posicona el puntero al final del archivo para añadir
Files.writeString(ruta, contenido, StandardOpenOption.CREATE, StandardOpenOption.APPEND);

b) Escribir usando la clase de utilidad Files: escribir las líneas dadas por una lista de String

Cada elemento de la lista será una línea nueva en el fichero:

var ruta = Path.of("archivo.txt");
List<String> lineas = Arrays.asList("Primera línea", "Segunda línea", "Tercera línea");
Files.write(ruta, lineas);

Aquí, por defecto, como en el caso anterior:

  • Si el fichero ruta existe se sobreescribirá, eliminando todo el contenido que pudiera tener.
  • Si el fichero ruta no existe se crea con lista de String.

Si quieres abrir en modo añadir tendrás que especificarlo en el método write.

c) Escritura de ficheros usando flujos tradicionales: con PrintWriter

Se puede usar PrintWriter para usar el método printf que permite una salida general de forma más fácil en algunos escenarios. Aquí sí estamos obligados a cerrar de forma explícita el fichero.

Aquí si tienes que seguir el flujo tradicional: open-write-flush-close:

var ruta = Path.of("archivo.txt");
try (var writer = new PrintWriter(Files.newBufferedWriter(ruta))) {
    var nombre = "Alice";
    var edad = 20;
    writer.printf(Locale.getDefault(), "Hola, %s, el año que viene tendrás %d años!%n", nombre, edad + 1);
} catch (IOException e) {
    System.err.println("Se lanzó una excepción: " + e);
}

Aquí he usado un bloque try-with-resources para cerrar el PrintWriter necesario para que se vuelque el contenido escrito en el fichero. También se puede usar directamente el método close:

PrintWriter writer = null;
try {
    var ruta = Path.of("archivo.txt");
    writer = new PrintWriter(Files.newBufferedWriter(ruta));
    var name = "Alice";
    var age = 20;
    writer.printf(Locale.getDefault(), "Hola, %s, el año que viene tendrás %d años!%n", name, age + 1);
} catch (IOException e) {
    System.err.println("Se lanzó una excepción: " + e);
} finally {
    if (writer != null) {
        writer.close();
    }
}

Por simplicidad, seguridad y para evitar dejar descriptores de archivo abiertos ante posibles errores, conviene usar siempre la estructura try-with-resources.

d) Escritura de ficheros usando flujos tradicionales: con BufferedWriter

Otra opción tradicional sería usar la clase BufferedWriter para escribir datos en ficheros (también tienes que cerrar el fichero de forma explícita):

try (var writer = Files.newBufferedWriter(path)) {
    var lineas = List.of("Línea 1", "Línea 2", "Línea 3", "Línea 4");
    for (String linea : lineas) {
        writer.write(linea);
        writer.newLine(); // añade salto de línea
    }
} catch (IOException e) {
    System.err.println("Se lanzó una excepción: " + e);
}

Resumen lectura/escritura de texto en Java

En la API de Java NIO/IO existe una simetría entre las operaciones de entrada de texto (BufferedReader / Files) y de salida (BufferedWriter / PrintWriter / Files).

Escenario Lectura (Input) Escritura (Output) Uso principal / Características
a) Contenido completo (String) Files.readString(path) Files.writeString(path, texto) Ficheros pequeños cargados o escritos enteros en RAM
b) Colección de líneas Files.readAllLines(path) Files.write(path, lista) Ficheros pequeños procesados como List
c) Procesado iterativo / Stream Files.lines(path) BufferedWriter (Files.newBufferedWriter) Ficheros grandes. Carga lazy (Stream) o escrituras continuas en bucle
d) Procesado iterativo tradicional Files.newBufferedReader(path) BufferedWriter Enfoque imperativo clásico (while) procesando línea a línea
e) Salida con formato — PrintWriter Permite usar printf y format (numéricos, fechas, cadenas)

Regla práctica de rendimiento y uso:

  • Para las opciones a) y b) la clase Files gestiona de forma transparente la apertura, volcado y cierre del fichero. Solo deben usarse con ficheros pequeños para evitar desbordamientos de memoria (OutOfMemoryError).
  • Para las opciones c), d) y e) es obligatorio cerrar los recursos (preferiblemente mediante una estructura try-with-resources) para asegurar el liberado del descriptor de archivo y el volcado automático del búfer (flush).
  • Por defecto, los métodos de escritura en Files sobrescriben el fichero. Para añadir contenido al final sin borrar el existente (append), se deben pasar las opciones StandardOpenOption.CREATE y StandardOpenOption.APPEND.

Author: Román Ginés Martínez Ferrández

Created: 2026-09-06 dom 17:25

Validate