1. Introduction
Il est temps de discuter des situations embêtantes où travailler avec des fichiers tourne en rencontre avec des erreurs (parfois assez mystérieuses). Si vous avez déjà rencontré des erreurs comme System.Text.DecoderFallbackException, alors vous connaissez déjà le sujet de première main !
Dans cette leçon nous verrons :
- Quels types d'erreurs liées aux encodages existent en .NET ;
- Comment se manifestent des fichiers corrompus ou incorrects ;
- Des exemples pratiques d'interception et de traitement de ces erreurs ;
- À quoi faire attention quand vous travaillez avec des fichiers d'autres personnes (ou avec des « bons vieux » fichiers trouvés sur un disque ancien).
Donc, si ASCII était trop simple et que Unicode était trop malin, il arrive parfois des fichiers que personne ne sait lire. C'est là que les exceptions apparaissent.
Pourquoi ça arrive ?
Quand vous ouvrez un fichier avec StreamReader, en précisant un encodage (ou en utilisant l'encodage par défaut), .NET suppose que tous les octets du fichier peuvent être correctement convertis en caractères. Mais si le fichier contient des octets qui dans cet encodage ne correspondent à aucun caractère, une erreur de décodage se produit.
2. Exceptions lors de la lecture de fichiers avec un encodage incorrect
L'exception la plus fréquente — DecoderFallbackException
Cette exception est levée par .NET quand il est impossible d'associer une séquence d'octets à un caractère dans l'encodage attendu.
Un exemple simple pour que tout soit clair :
// Supposons un vieux fichier en Windows-1251 (cyrillique)
string win1251File = "win1251_test.txt";
File.WriteAllText(win1251File, "Привет, мир!", Encoding.GetEncoding("windows-1251"));
try
{
// Essayons de lire ce fichier comme UTF-8
using var reader = new StreamReader(win1251File, Encoding.UTF8);
string content = reader.ReadToEnd();
Console.WriteLine(content); // ...et affichera du charabia (ou lancera une exception)
}
catch (DecoderFallbackException ex)
{
Console.WriteLine("Erreur de décodage : " + ex.Message);
}
La plupart du temps, en lisant un fichier sauvegardé en Windows-1251 comme UTF-8, au lieu d'un texte lisible on obtiendra une série de « mojibake ». Par défaut StreamReader dans ces cas ne lance pas d'exception mais substitue un caractère de remplacement "�" à la place des octets incompréhensibles. Toutefois si vous configurez explicitement l'encodage avec un DecoderExceptionFallback strict ou si le flux contient des octets particulièrement « impossibles à digérer », DecoderFallbackException sera lancé.
DecoderFallbackException en détail
- Quand elle survient : lors de la tentative de lecture d'une séquence d'octets qui ne peut pas être convertie en caractères avec l'encodage courant.
- Que faire : lire le fichier avec le bon encodage ! Si vous ne savez pas quel encodage est utilisé, essayez de le deviner (parfois on peut se baser sur le BOM ou le nom du fichier) ou demandez à la personne qui a créé le fichier.
3. Exemple avec un fichier réellement corrompu
Maintenant complexifions la tâche. Imaginons que le fichier soit corrompu : dans la séquence d'octets il y a des fragments de caractères tronqués. Ça arrive lors d'une écriture interrompue, d'erreurs réseau, de conversions ratées ou en bricolant le fichier avec un cutter... au sens littéral : le fichier est « coupé » n'importe où.
Créons un fichier « cassé »
// Écrivons une chaîne valide en UTF-8
byte[] valid = Encoding.UTF8.GetBytes("Привет, мир!");
// Et maintenant créons un tableau d'octets incorrect (on coupe une partie d'un caractère)
byte[] corrupted = new byte[valid.Length - 1];
Array.Copy(valid, corrupted, valid.Length - 1); // On a coupé le dernier octet
// Sauvegardons le fichier
File.WriteAllBytes("corrupted.txt", corrupted);
try
{
using var reader = new StreamReader("corrupted.txt", Encoding.UTF8);
string s = reader.ReadToEnd();
Console.WriteLine("Texte lu : " + s);
}
catch (DecoderFallbackException ex)
{
Console.WriteLine("Fichier corrompu ! " + ex.Message);
}
En sortie : .NET ne pourra pas reconstruire correctement le dernier caractère. Par défaut il le remplacera par le caractère spécial "�" (ou "?") ou, si l'encodage est configuré en conséquence, lancera DecoderFallbackException.
4. Stratégies de fallback : peut-on éviter l'exception ?
Parfois, quand un caractère est « incompréhensible », on préfère ne pas lancer d'erreur mais le remplacer par « ? » ou autre chose. Pour ça .NET propose des stratégies de fallback.
Exemple : remplacer le caractère au lieu de lancer une exception
// Tableau avec une séquence invalide pour UTF-8
byte[] data = { 0xD0, 0x9F, 0xD1, 0x80, 0xD0, 0xB8, 0xD0, 0xB2, 0xD0, 0xB5, 0xD1, 0x82, 0xD1 }; // Le dernier octet est tronqué
File.WriteAllBytes("broken_utf8.txt", data);
// Stratégie de fallback : remplacer le caractère problématique par un point d'interrogation
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("Texte (avec remplacement des erreurs) : " + s);
Résultat : le fichier sera lu avec les caractères inconnus remplacés par "?". Vous évitez ainsi un plantage mais obtenez un texte qui n'est pas entièrement « natif ».
5. Problèmes avec le BOM et incompatibilités
Rappel : BOM — Byte Order Mark, une séquence spéciale d'octets au début du fichier qui indique « salut, je suis tel encodage ! ».
Quand le BOM peut poser problème
- Si le fichier contient un BOM et que l'application ne sait pas le lire, le premier caractère de la ligne sera étrange (par exemple "" ou un caractère invisible).
- Parfois l'absence de BOM mène à une mauvaise détection de l'encodage.
Exceptions liées au BOM
En général C# sait gérer le BOM à la lecture, mais si vous spécifiez un encodage incorrect ou coupez manuellement le BOM, vous risquez d'obtenir :
- Un caractère inattendu au début (par exemple le caractère "�") ;
- Une exception si l'encodage est configuré pour la lancer et que le BOM est interprété comme une séquence d'octets invalide.
Conseil pratique : spécifiez toujours explicitement l'encodage lors de la lecture/écriture si le type d'encodage est important pour vous.
6. Autres exceptions et scénarios intéressants
Mauvais encodage à l'écriture
Quand vous essayez d'écrire une chaîne contenant des caractères non pris en charge par l'encodage choisi. Par exemple, essayez d'enregistrer l'emoji « 😊 » dans un fichier avec Encoding.ASCII :
try
{
using var writer = new StreamWriter("ascii.txt", false, Encoding.ASCII);
writer.WriteLine("Ceci est un test 😊");
}
catch (EncoderFallbackException ex)
{
Console.WriteLine("Erreur d'encodage : " + ex.Message);
}
Résultat : vous obtiendrez soit une exception EncoderFallbackException, soit le caractère sera remplacé par "?" — cela dépend de la stratégie de fallback de l'encodage.
Problème lors de la conversion entre encodages (perte de données)
En convertissant un fichier, vous pouvez involontairement perdre des données si l'encodage cible ne contient pas tous les caractères de l'encodage source (par exemple convertir de UTF-8 en Windows-1251 un fichier contenant du texte japonais).
Corruption due au disque, au réseau ou au « réglage manuel »
Si des octets aléatoires ou corrompus se retrouvent dans le fichier (après une panne disque ou en éditant un fichier binaire avec un éditeur texte), la tentative de lecture lancera souvent des exceptions de décodage.
7. Comment intercepter et traiter les erreurs en pratique ?
Comme les erreurs peuvent survenir à différentes étapes du travail avec les fichiers, il est recommandé de :
- Utiliser des blocs try-catch pour attraper les exceptions — principalement DecoderFallbackException et EncoderFallbackException.
- Ne pas hésiter à informer l'utilisateur : si le fichier est corrompu ou si l'encodage est « le mauvais », mieux vaut le dire plutôt que d'afficher du texte étrange.
- Automatiser dans la mesure du possible la détection d'encodage (par exemple via le BOM ou avec des bibliothèques comme Ude), mais toujours offrir à l'utilisateur la possibilité de choisir l'encodage en cas d'échec.
Structure type du code :
try
{
using var reader = new StreamReader("file.txt", Encoding.GetEncoding("windows-1251"));
string s = reader.ReadToEnd();
Console.WriteLine(s);
}
catch (DecoderFallbackException ex)
{
Console.WriteLine($"Impossible de lire le fichier : {ex.Message}");
// On peut proposer à l'utilisateur d'essayer un autre encodage
}
catch (IOException ex)
{
Console.WriteLine($"Erreur d'E/S : {ex.Message}");
}
8. Quelques « pièges » typiques
Tenter de lire un fichier UTF-8 en Windows-1251 : au mieux vous verrez du « mojibake », au pire vous obtiendrez une exception (si l'encodage est configuré pour la lancer).
Écrire en ASCII un fichier avec du texte russe : tout ce qui n'est pas l'alphabet anglais sera remplacé par "?" ou provoquera une EncoderFallbackException.
Lire un fichier sans BOM comme UTF-8 alors qu'il est en UTF-16 : vous lirez du charabia ou vous ne pourrez même pas lire le fichier correctement.
Fichiers sans encodage explicite provenant de sources non sûres : restez vigilant : même si le fichier s'ouvre « sans erreurs », cela ne garantit pas un résultat correct.
GO TO FULL VERSION