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.