CodeGym /Cursos /JAVA 25 SELF /Gestión de archivos dañados y recuperación de datos

Gestión de archivos dañados y recuperación de datos

JAVA 25 SELF
Nivel 38 , Lección 2
Disponible

1. Señales de archivos dañados

En un mundo ideal, los archivos siempre se leen y se escriben sin errores, y los datos en ellos son como pan recién horneado: suaves, aromáticos, uniformes e intactos. Pero en la realidad, los archivos pueden estar «corruptos», «torcidos», «a medias» o «en un formato incorrecto». Las causas pueden ser diversas. Por ejemplo, un fallo al escribir en disco (por un corte repentino de energía), errores de red durante la transferencia del archivo, avería del soporte (el viejo y nada amable bad sector). O la edición manual de un archivo en un programa inadecuado puede dañarlo, igual que la discrepancia entre el formato esperado y el real de los datos.

En Java, este tipo de situaciones suelen manifestarse mediante excepciones al leer/escribir y, a veces, mediante comportamientos extraños del programa (por ejemplo, los datos se agotan de repente o aparece galimatías).

Excepciones al leer

La señal más evidente — excepciones inesperadas. Algunas de ellas:

  • EOFException — fin de archivo inesperado (End Of File). Esperabas que el archivo tuviera más datos, pero no los hay.
  • MalformedInputException (o, en APIs antiguas, MalformedInputException de NIO) — el archivo no se ajusta a la codificación o estructura esperada.
  • ZipException — si intentas leer un archivo comprimido como un archivo normal.
  • StreamCorruptedException — al leer objetos serializados si el archivo está dañado.

Desajuste de formato de datos

A veces el archivo se lee sin lanzar excepción, pero el contenido no corresponde al formato esperado:

  • Se esperaba una cadena y se obtuvo un conjunto de símbolos incomprensibles.
  • Se esperaba una cantidad determinada de números y hay menos.
  • Se esperaba un archivo en formato CSV y resulta que es JSON (o al revés).

Ejemplo real

Supongamos que escribes una aplicación que guarda una lista de tareas en un archivo de texto. El programa espera que cada línea sea una tarea independiente. Pero el usuario decidió abrir el archivo en Excel, hizo cambios, lo guardó en otro formato... y ahora tu programa no puede leer el archivo.

2. Estrategias para tratar archivos dañados

Registro y comunicación al usuario

Primera regla: ¡no entrar en pánico! (y no hacer que el usuario entre en pánico). Siempre registra el error y avisa al usuario si algo ha salido mal. No es necesario describirle todos los horrores del stack de Java.

try {
    // lectura del archivo
} catch (EOFException e) {
    System.err.println("El archivo terminó de forma inesperada. Es posible que esté dañado.");
    // registramos los detalles
    e.printStackTrace();
}

Intento de recuperación parcial

A veces se puede «salvar» al menos parte de los datos. Por ejemplo, si el archivo se lee línea a línea, se pueden procesar todas las líneas hasta el primer error.

Uso de copias de seguridad (backup)

Las aplicaciones serias suelen crear copias de seguridad de archivos importantes antes de escribir. Si el archivo principal está dañado, puedes intentar recuperar los datos desde la copia de seguridad.

3. Práctica: lectura de un archivo con fin inesperado (EOF)

Situación clásica

Supongamos que tenemos un archivo binario en el que se han escrito secuencialmente números de tipo int. El programa espera que haya exactamente 5, pero el archivo se dañó y solo se guardaron 3.

import java.io.*;

public class DamagedFileExample {
    public static void main(String[] args) {
        String filename = "numbers.bin";

        // Para el ejemplo: creamos un archivo con 3 números (en lugar de 5)
        try (DataOutputStream out = new DataOutputStream(new FileOutputStream(filename))) {
            out.writeInt(42);
            out.writeInt(7);
            out.writeInt(2024);
            // out.writeInt(1); out.writeInt(2); // no los escribimos a propósito!
        } catch (IOException e) {
            System.err.println("Error al crear el archivo: " + e.getMessage());
        }

        // Ahora intentemos leer 5 números
        try (DataInputStream in = new DataInputStream(new FileInputStream(filename))) {
            for (int i = 0; i < 5; i++) {
                int number = in.readInt();
                System.out.println("Número leído: " + number);
            }
        } catch (EOFException e) {
            System.err.println("¡El archivo terminó de forma inesperada! Es posible que esté dañado.");
        } catch (IOException e) {
            System.err.println("Error de lectura: " + e.getMessage());
        }
    }
}

Salida:

Número leído: 42
Número leído: 7
Número leído: 2024
¡El archivo terminó de forma inesperada! Es posible que esté dañado.

Lectura hasta el primer error

A menudo es razonable leer los datos en un bucle hasta que se produzca una excepción. Así se puede obtener al menos parte de la información.

try (DataInputStream in = new DataInputStream(new FileInputStream(filename))) {
    while (true) {
        try {
            int number = in.readInt();
            System.out.println("Número leído: " + number);
        } catch (EOFException e) {
            System.out.println("Se acabaron los datos (o el archivo está dañado).");
            break;
        }
    }
} catch (IOException e) {
    System.err.println("Error de lectura: " + e.getMessage());
}

4. Trabajo con archivos de texto y codificaciones

Problemas de codificación

Si el archivo se guardó con una codificación y se lee con otra, pueden producirse errores de decodificación:

import java.nio.charset.*;

try (BufferedReader reader = new BufferedReader(
        new InputStreamReader(new FileInputStream("tasks.txt"), "UTF-8"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (MalformedInputException e) {
    System.err.println("¡Error de codificación! El archivo está dañado o no se guardó en UTF-8.");
} catch (IOException e) {
    System.err.println("Error de lectura: " + e.getMessage());
}

Importante: A veces, en lugar de una excepción, verás «caracteres ilegibles» — esto también es un indicio de corrupción o de codificación incorrecta.

¿Cómo manejarlo?

  • Informar al usuario del problema.
  • Intentar abrir el archivo con otra codificación.
  • Si los datos son críticos, proponer recuperar desde una copia de seguridad.

5. Recuperación de datos: estrategias

Lectura de datos parcialmente válidos

Si la estructura del archivo lo permite, se pueden «extraer» al menos los datos que se hayan podido leer hasta el error. Por ejemplo, si el archivo es una lista de líneas (una tarea — una línea), se pueden procesar todas las líneas hasta el fallo.

try (BufferedReader reader = new BufferedReader(new FileReader("tasks.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        // procesar la línea
    }
} catch (IOException e) {
    System.err.println("Error de lectura: " + e.getMessage());
    // Puedes guardar los datos ya leídos o proponer al usuario una recuperación
}

Uso de archivos de copia de seguridad

Si haces previamente una copia del archivo (por ejemplo, tasks.txt.bak), puedes recuperar los datos desde ella:

File original = new File("tasks.txt");
File backup = new File("tasks.txt.bak");

if (!original.exists() && backup.exists()) {
    // Copiamos el backup en el lugar del original
    Files.copy(backup.toPath(), original.toPath(), StandardCopyOption.REPLACE_EXISTING);
    System.out.println("La recuperación desde la copia de seguridad se ha completado.");
}

Sumas de verificación y validación

Para archivos importantes, se puede almacenar una suma de verificación (por ejemplo, MD5 o SHA-256) y, cada vez que se abra el archivo, compararla con la actual. Si no coincide, el archivo está dañado.

// Esquema aproximado (se omite la implementación del hashing para simplificar)
String expectedHash = "..."; // suma guardada previamente
String actualHash = calculateFileHash("tasks.txt");
if (!expectedHash.equals(actualHash)) {
    System.out.println("¡El archivo tasks.txt está dañado! Intenta recuperar desde la copia de seguridad.");
}

6. Errores típicos al manejar archivos dañados

Error n.º 1: No se comprueba el formato del archivo. Si esperas que cada línea sea, por ejemplo, un número, y resulta ser texto, aparecerá NumberFormatException. Es mejor validar los datos a medida que se leen.

Error n.º 2: Falta de try-with-resources. Si no utilizas try-with-resources, el archivo puede quedar «colgado» (sin cerrar) incluso ante un error, lo que dificultará su recuperación o eliminación.

Error n.º 3: Sobrescribir un archivo dañado sin crear una copia de seguridad. Si, ante un error, sobrescribes el archivo inmediatamente, reduces las probabilidades de recuperación. Es mejor guardar primero un backup.

Error n.º 4: Recuperación poco informativa. El usuario debe saber que el archivo estaba dañado y que se recuperó desde una copia; de lo contrario, puede no entender por qué parte de los datos ha desaparecido.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION