Saltar al contenido principal

Ficheros con Path, Files y recursos

Un programa pierde sus datos al terminar si solo viven en memoria. La API moderna separa dos ideas: Path representa una ruta y Files realiza operaciones sobre ella.

Rutas y directorio de trabajo​

Path ruta = Path.of("datos", "tareas.txt");
System.out.println(ruta.toAbsolutePath());

Una ruta relativa se resuelve desde el directorio de trabajo. Una absoluta parte desde la raíz del sistema. Evita asumir que el programa siempre se ejecutará desde el editor.

Crear y comprobar​

Path directorio = Path.of("datos");
Files.createDirectories(directorio);
Path fichero = directorio.resolve("tareas.txt");
System.out.println(Files.exists(fichero));

createDirectories crea también padres ausentes y no falla si ya existen.

Escribir y leer UTF-8​

Files.writeString(
fichero,
"Estudiar Java\n",
StandardCharsets.UTF_8
);
String contenido = Files.readString(fichero, StandardCharsets.UTF_8);

Sin opciones, writeString crea el fichero o sobrescribe su contenido. Para añadir:

Files.writeString(
fichero,
"Practicar\n",
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.APPEND
);

readAllLines devuelve una List<String> y Files.write puede escribir varias líneas. Elegir entre snapshot completo y añadido debe ser una decisión explícita.

IOException​

Las operaciones pueden fallar por permisos, rutas inválidas o problemas del dispositivo. IOException es checked: debemos capturarla con un propósito o propagarla.

try {
String texto = Files.readString(fichero, StandardCharsets.UTF_8);
System.out.println(texto);
} catch (IOException error) {
System.out.println("No se pudo leer " + fichero + ": " + error.getMessage());
}

Recursos y cierre automático​

Para contenido grande puede interesar procesar con buffer. try-with-resources cierra el recurso aunque ocurra un error:

try (BufferedReader lector = Files.newBufferedReader(fichero, StandardCharsets.UTF_8)) {
String linea;
while ((linea = lector.readLine()) != null) {
System.out.println(linea);
}
}

El objeto declarado entre paréntesis implementa AutoCloseable; Java llama a close() automáticamente. Esto evita cierres manuales en finally. BufferedWriter funciona del mismo modo para escritura.

No usamos Files.lines() aquí porque devuelve un Stream, concepto de JV3-08. File, FileReader y FileWriter existen como APIs clásicas, pero Path y Files son el flujo recomendado. No serializaremos objetos.

Ejemplo completo​

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

public class FicherosDemo {
public static void main(String[] args) throws IOException {
Path directorio = Path.of("datos-demo");
Path fichero = directorio.resolve("notas.txt");
Files.createDirectories(directorio);
Files.writeString(fichero, "uno\n", StandardCharsets.UTF_8);
Files.writeString(fichero, "dos\n", StandardCharsets.UTF_8,
StandardOpenOption.CREATE, StandardOpenOption.APPEND);

try (BufferedReader lector = Files.newBufferedReader(fichero, StandardCharsets.UTF_8)) {
String linea;
while ((linea = lector.readLine()) != null) System.out.println(linea);
}
}
}

Errores habituales​

  • Confundir la ruta con el contenido.
  • Crear el fichero sin asegurar el directorio padre.
  • Depender del charset por defecto.
  • Sobrescribir cuando se quería añadir, o acumular cuando se quería snapshot.
  • Abrir un recurso y no cerrarlo.
  • Silenciar IOException.

Practica: registro local​

Enunciado y solución​

Crea salida/registro.txt, escribe dos líneas UTF-8, léelas y muestra si existe.

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public class RegistroDemo {
public static void main(String[] args) throws IOException {
Path ruta = Path.of("salida", "registro.txt");
Files.createDirectories(ruta.getParent());
Files.write(ruta, List.of("alta", "consulta"), StandardCharsets.UTF_8);
System.out.println(Files.exists(ruta));
for (String linea : Files.readAllLines(ruta, StandardCharsets.UTF_8)) {
System.out.println(linea);
}
}
}

La práctica crea sus padres, fija el charset y deja que la excepción se propague hasta el límite del programa.