CodeGym /Cursos /C# SELF /Archivos corruptos y codificaciones

Archivos corruptos y codificaciones

C# SELF
Nivel 38 , Lección 3
Disponible

1. Introducción

Es hora de hablar de esas situaciones molestosas en las que trabajar con archivos se vuelve un encuentro con errores (a veces bastante enigmáticos). Si alguna vez te has topado con errores como System.Text.DecoderFallbackException, ya conoces el tema de primera mano.

En esta lección veremos:

  • Qué tipos de errores relacionados con codificaciones existen en .NET;
  • Cómo se manifiestan los archivos corruptos o incorrectos;
  • Ejemplos prácticos de captura y manejo de esos errores;
  • En qué fijarse al trabajar con archivos ajenos (o con esos “viejos y buenos” archivos encontrados en un disco antiguo).

Así que si ASCII era demasiado simple y Unicode demasiado listo, a veces aparecen archivos que nadie sabe leer. Ahí es cuando aparecen las excepciones.

¿Por qué pasa esto?

Cuando abres un archivo con StreamReader, especificando una codificación (o usando la codificación por defecto), .NET asume que todos los bytes del archivo pueden convertirse correctamente a caracteres. Pero si el archivo contiene bytes que en esa codificación no corresponden a ningún carácter, ocurre un error de decodificación.

2. Excepciones al leer archivos con codificación incorrecta

La excepción más común — DecoderFallbackException

Esta excepción la lanza .NET cuando no puede mapear una secuencia de bytes a un carácter en la codificación esperada.

Un ejemplo simple para que quede claro:


// Supongamos un archivo antiguo en Windows-1251 (cirílico)
string win1251File = "win1251_test.txt";
File.WriteAllText(win1251File, "Privet, mir!", Encoding.GetEncoding("windows-1251"));

try
{
    // Intentamos leer ese archivo como UTF-8
    using var reader = new StreamReader(win1251File, Encoding.UTF8);
    string content = reader.ReadToEnd();
    Console.WriteLine(content); // ...y mostrará caracteres raros (o lanzará una excepción)
}
catch (DecoderFallbackException ex)
{
    Console.WriteLine("Error de decodificación: " + ex.Message);
}

En la mayoría de los casos, al leer un archivo guardado en Windows-1251 como UTF-8, en lugar de texto legible obtendrás un montón de “caracteres raros”. Por defecto StreamReader en estas situaciones no lanza una excepción, sino que coloca el carácter de reemplazo "�" en lugar de los bytes incomprensibles. Sin embargo, si configuras explícitamente la codificación con un DecoderExceptionFallback estricto o si en el flujo hay bytes especialmente “indigeribles”, se lanzará DecoderFallbackException.

DecoderFallbackException en detalle

  • Cuándo ocurre: al intentar leer una secuencia de bytes que no puede convertirse a caracteres con la codificación actual.
  • Qué hacer: lee el archivo con la codificación correcta. Si no sabes en qué codificación está el archivo, intenta adivinar (a veces se puede por el BOM o por el nombre del archivo) o pregunta a quien creó el archivo.

3. Ejemplo con un archivo explícitamente corrupto

Ahora complicamos un poco. Imagina que el archivo está dañado: dentro de la secuencia de bytes hay fragmentos de caracteres cortados. Eso pasa cuando se interrumpe la escritura del archivo, por errores de red, conversiones fallidas o por usar un cutter... en sentido literal: el archivo está “cortado” en cualquier sitio.

Creemos un archivo “roto”


// Escribimos una cadena válida en UTF-8
byte[] valid = Encoding.UTF8.GetBytes("Privet, mir!");
// Y ahora creamos un array de bytes incorrecto (cortamos parte de un carácter)
byte[] corrupted = new byte[valid.Length - 1];
Array.Copy(valid, corrupted, valid.Length - 1); // Cortamos el último byte
        
// Guardamos el archivo
File.WriteAllBytes("corrupted.txt", corrupted);

try
{
    using var reader = new StreamReader("corrupted.txt", Encoding.UTF8);
    string s = reader.ReadToEnd();
    Console.WriteLine("Texto leído: " + s);
}
catch (DecoderFallbackException ex)
{
    Console.WriteLine("¡Archivo corrupto! " + ex.Message);
}

Como resultado: .NET no podrá reconstruir correctamente el último carácter. Por defecto lo reemplazará por el símbolo especial "�" (o por "?") o, si la codificación está configurada para ello, lanzará DecoderFallbackException.

4. Estrategias de fallback: ¿se puede evitar la excepción?

A veces, cuando un carácter es “incomprensible”, prefieres no lanzar un error y, por ejemplo, reemplazarlo por “?” u otra cosa. Para eso en .NET existen las llamadas estrategias de fallback.

Ejemplo: usamos reemplazo de carácter en vez de excepción


// Array con una secuencia inválida para UTF-8
byte[] data = { 0xD0, 0x9F, 0xD1, 0x80, 0xD0, 0xB8, 0xD0, 0xB2, 0xD0, 0xB5, 0xD1, 0x82, 0xD1 }; // Último byte cortado
File.WriteAllBytes("broken_utf8.txt", data);

// Estrategia de fallback: reemplazar el carácter problemático por '?'
var encodingWithFallback = Encoding.GetEncoding(
    "UTF-8",
    new EncoderReplacementFallback("?"),
    new DecoderReplacementFallback("?")
);

using var reader = new StreamReader("broken_utf8.txt", encodingWithFallback);
string s = reader.ReadToEnd();
Console.WriteLine("Texto (con reemplazo de errores): " + s);

Resultado: se leerá el texto del archivo con los caracteres desconocidos reemplazados por "?". Así evitas que la aplicación se caiga, pero obtienes un texto que no es exactamente “orig inal”.

5. Problemas con BOM e incompatibilidades

Recordemos que BOM es Byte Order Mark, una secuencia especial de bytes al inicio del archivo que dice “hola, soy esta codificación”.

Cuándo el BOM puede causar dolor de cabeza

  • Si el archivo contiene BOM y la aplicación no sabe manejarlo, el primer carácter de la línea será extraño (por ejemplo, "" o un carácter invisible).
  • A veces la ausencia de BOM provoca una detección incorrecta de la codificación.

Excepciones relacionadas con BOM

Normalmente C# sabe “digerir” el BOM al leer, pero si especificas una codificación incorrecta o quitas manualmente el BOM, corres el riesgo de obtener:

  • Un carácter inesperado al inicio (por ejemplo, el símbolo "�");
  • Una excepción si la codificación está configurada para lanzarla y el BOM se interpreta como una secuencia de bytes inválida.

Consejo práctico: siempre especifica la codificación al leer/escribir si el tipo de codificación es importante para ti.

6. Otras excepciones y escenarios interesantes

Codificación incorrecta al escribir

Cuando intentas escribir una cadena que contiene caracteres que la codificación elegida no soporta. Por ejemplo, intenta guardar un emoji “😊” en un archivo con Encoding.ASCII:


try
{
    using var writer = new StreamWriter("ascii.txt", false, Encoding.ASCII);
    writer.WriteLine("Esto es una prueba 😊");
}
catch (EncoderFallbackException ex)
{
    Console.WriteLine("Error de codificación: " + ex.Message);
}

Resultado: obtendrás o bien EncoderFallbackException, o el carácter será reemplazado por "?" — depende de la estrategia de fallback configurada.

Pérdida de datos al convertir entre codificaciones

Al convertir un archivo puedes perder datos si la codificación destino no tiene todos los caracteres de la original (por ejemplo, convertir de UTF-8 a Windows-1251 un archivo que contiene texto en japonés).

Corrupción por disco, red o “edición manual”

Si bytes aleatorios o corruptos entran en el archivo (por ejemplo después de un fallo de disco o al editar un archivo binario con un editor de texto), intentar leer ese archivo suele lanzar excepciones de decodificación.

7. ¿Cómo capturar y manejar errores en la práctica?

Como los errores pueden surgir en distintas etapas del trabajo con archivos, se recomienda:

  • Usar bloques try-catch para atrapar excepciones — sobre todo DecoderFallbackException y EncoderFallbackException.
  • No tener reparo en informar al usuario: si el archivo está corrupto o la codificación “no es la correcta”, es mejor decirlo claramente que mostrar texto raro.
  • Cuando sea posible, automatizar la detección de codificación (por ejemplo, por el BOM o con librerías como Ude), pero siempre dar la opción al usuario de elegir la codificación si falla la detección automática.

Estructura típica de código:


try
{
    using var reader = new StreamReader("file.txt", Encoding.GetEncoding("windows-1251"));
    string s = reader.ReadToEnd();
    Console.WriteLine(s);
}
catch (DecoderFallbackException ex)
{
    Console.WriteLine($"No se pudo leer el archivo: {ex.Message}");
    // Puedes sugerir al usuario intentar con otra codificación
}
catch (IOException ex)
{
    Console.WriteLine($"Error de E/S: {ex.Message}");
}

8. Unos “errores típicos”

Intentar leer un archivo UTF-8 como Windows-1251: en el mejor de los casos verás “caracteres raros”, en el peor — una excepción (si la codificación está configurada para lanzarla).

Escribir en ASCII un archivo con texto en ruso: todo lo que no sea el alfabeto inglés se reemplazará por "?" o provocará EncoderFallbackException.

Leer un archivo sin BOM como UTF-8 cuando en realidad es UTF-16: obtendrás galimatías o incluso no podrás leer el archivo en absoluto.

Archivos sin codificación explícita de fuentes no confiables: siempre mantén la guardia: aunque un archivo se abra “sin errores”, eso no garantiza que el contenido sea correcto.

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