CodeGym /Cursos /JAVA 25 SELF /Tratamento de arquivos corrompidos e recuperação de dados...

Tratamento de arquivos corrompidos e recuperação de dados

JAVA 25 SELF
Nível 38 , Lição 2
Disponível

1. Sinais de arquivos corrompidos

Em um mundo ideal, os arquivos sempre são lidos e gravados sem erros, e os dados neles — como pãezinhos recém-assados: macios, perfumados, uniformes, inteiros. Mas, na realidade, arquivos podem estar “quebrados”, “tortos”, “pela metade” ou “no formato errado”. As causas podem ser diferentes. Por exemplo, falha ao gravar no disco (em caso de desligamento repentino), erros de rede durante a transferência do arquivo, defeito no meio de armazenamento (o velho e nada bom bad sector). Ou o simples fato de editar o arquivo manualmente em um programa inadequado pode danificá-lo, assim como a divergência entre o formato esperado e o real.

Em Java, essas situações geralmente se manifestam por exceções na leitura/gravação e, às vezes, por um comportamento estranho do programa (por exemplo, os dados acabam de repente ou aparece uma “abracadabra” de caracteres).

Exceções na leitura

O sinal mais evidente é o lançamento de exceções inesperadas. Aqui vão algumas:

  • EOFException — fim inesperado do arquivo (End Of File). Você esperava que ainda houvesse dados, mas não há.
  • MalformedInputException (ou, em APIs antigas do NIO, MalformedInputException) — o arquivo não corresponde à codificação ou estrutura esperada.
  • ZipException — se você tentar ler um arquivo compactado como um arquivo comum.
  • StreamCorruptedException — ao ler objetos serializados, se o arquivo estiver corrompido.

Incompatibilidade de formato de dados

Às vezes o arquivo é lido sem exceção, mas o conteúdo não corresponde ao formato esperado:

  • Você esperava uma string e obteve um conjunto de caracteres sem sentido.
  • Você esperava uma quantidade específica de números, mas há menos.
  • Você esperava um arquivo no formato CSV, mas é JSON (ou vice-versa).

Exemplo do dia a dia

Suponha que você escreva um aplicativo que armazena uma lista de tarefas em um arquivo de texto. O programa espera que cada linha seja uma tarefa separada. Mas o usuário resolveu abrir o arquivo no Excel, fez alterações, salvou em outro formato... e agora seu programa não consegue ler o arquivo.

2. Estratégias para lidar com arquivos corrompidos

Registro em log e comunicação com o usuário

Primeira regra: não entre em pânico! (e não deixe o usuário entrar em pânico). Sempre registre o erro em log e avise o usuário se algo deu errado. Mas não é necessário descrever para ele todo o horror da pilha de chamadas do Java.

try {
    // leitura do arquivo
} catch (EOFException e) {
    System.err.println("O arquivo terminou inesperadamente. Ele pode estar corrompido.");
    // registrar detalhes do log
    e.printStackTrace();
}

Tentativa de recuperação parcial

Às vezes é possível “salvar” pelo menos parte dos dados. Por exemplo, se o arquivo for lido linha a linha, você pode processar todas as linhas até o primeiro erro.

Uso de cópias de segurança (backup)

Programas sérios costumam criar cópias de segurança de arquivos importantes antes de gravar. Se o arquivo principal estiver corrompido, é possível tentar recuperar os dados a partir do backup.

3. Prática: leitura de arquivo com término inesperado (EOF)

Situação clássica

Suponha que temos um arquivo binário no qual foram gravados, sequencialmente, números do tipo int. O programa espera exatamente 5, mas o arquivo foi corrompido e apenas 3 foram gravados.

import java.io.*;

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

        // Para o exemplo: criamos um arquivo com 3 números (em vez 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); // não gravamos de propósito!
        } catch (IOException e) {
            System.err.println("Erro ao criar o arquivo: " + e.getMessage());
        }

        // Agora vamos tentar ler 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 lido: " + number);
            }
        } catch (EOFException e) {
            System.err.println("O arquivo terminou inesperadamente! Talvez ele esteja corrompido.");
        } catch (IOException e) {
            System.err.println("Erro de leitura: " + e.getMessage());
        }
    }
}

Saída:

Número lido: 42
Número lido: 7
Número lido: 2024
O arquivo terminou inesperadamente! Talvez ele esteja corrompido.

Leitura até o primeiro erro

Frequentemente é sensato ler os dados em um laço até que ocorra uma exceção. Assim é possível obter ao menos parte das informações.

try (DataInputStream in = new DataInputStream(new FileInputStream(filename))) {
    while (true) {
        try {
            int number = in.readInt();
            System.out.println("Número lido: " + number);
        } catch (EOFException e) {
            System.out.println("Os dados terminaram (ou o arquivo está corrompido).");
            break;
        }
    }
} catch (IOException e) {
    System.err.println("Erro de leitura: " + e.getMessage());
}

4. Trabalhando com arquivos de texto e codificações

Problema de codificação

Se o arquivo foi gravado em uma codificação e é lido em outra, erros de decodificação podem ocorrer:

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("Erro de codificação! O arquivo está corrompido ou não foi gravado em UTF-8.");
} catch (IOException e) {
    System.err.println("Erro de leitura: " + e.getMessage());
}

Importante: Às vezes, em vez de uma exceção, você verá caracteres ilegíveis — isso também é um sinal de corrupção ou de codificação incorreta.

Como tratar?

  • Informar o usuário sobre o problema.
  • Tentar abrir o arquivo em outra codificação.
  • Se os dados forem críticos — sugerir a recuperação a partir de uma cópia de segurança.

5. Recuperação de dados: estratégias

Leitura de dados parcialmente válidos

Se a estrutura do arquivo permitir, é possível “extrair” pelo menos os dados que puderam ser lidos até o erro. Por exemplo, se o arquivo for uma lista de linhas (uma tarefa por linha), você pode processar todas as linhas até a falha.

try (BufferedReader reader = new BufferedReader(new FileReader("tasks.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        // processar a linha
    }
} catch (IOException e) {
    System.err.println("Erro de leitura: " + e.getMessage());
    // Você pode salvar os dados já lidos ou oferecer ao usuário uma recuperação
}

Uso de arquivos de backup

Se você cria previamente uma cópia do arquivo (por exemplo, tasks.txt.bak), pode recuperar os dados a partir dela:

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

if (!original.exists() && backup.exists()) {
    // Copiamos o backup para o lugar do original
    Files.copy(backup.toPath(), original.toPath(), StandardCopyOption.REPLACE_EXISTING);
    System.out.println("Recuperação a partir da cópia de segurança concluída.");
}

Checksums e validação

Para arquivos importantes, você pode armazenar uma soma de verificação (por exemplo, MD5 ou SHA-256) e, a cada abertura do arquivo, compará-la com a atual. Se não coincidir — o arquivo está corrompido.

// Esquema aproximado (implementação do hash omitida para simplificar)
String expectedHash = "..."; // soma salva anteriormente
String actualHash = calculateFileHash("tasks.txt");
if (!expectedHash.equals(actualHash)) {
    System.out.println("O arquivo tasks.txt está corrompido! Tente recuperar a partir de uma cópia de segurança.");
}

6. Erros comuns ao lidar com arquivos corrompidos

Erro №1: O formato do arquivo não é verificado. Se você espera que cada linha seja, por exemplo, um número, mas há texto — ocorrerá NumberFormatException. É melhor validar os dados à medida que são lidos.

Erro №2: Ausência de try-with-resources. Se você não usar try-with-resources, o arquivo pode ficar “pendurado” (não fechado) mesmo em caso de erro, o que dificultará a recuperação ou exclusão.

Erro №3: Regravar o arquivo corrompido sem criar uma cópia de segurança. Se, ao ocorrer um erro, você regravar o arquivo imediatamente, a chance de recuperação diminui. É melhor primeiro salvar um backup.

Erro №4: Recuperação não informativa. O usuário deve saber que o arquivo foi corrompido e recuperado a partir de uma cópia — caso contrário, ele pode não entender por que parte dos dados desapareceu.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION