1. Introduction
Les opérations sur les fichiers sont presque toujours liées à des circonstances externes au programme : le fichier peut être supprimé, déplacé, verrouillé par une autre application, l'utilisateur peut soudainement ne plus avoir les droits ou l'espace disque peut manquer. Tout cela peut conduire à des exceptions — des situations particulières où l'exécution du programme est interrompue et un objet exception est "lancé" (Exception). Si vous ne capturez pas et ne traitez pas une telle exception, votre programme se terminera avec une erreur et affichera à l'utilisateur un vilain texte rouge dans la console (ou, pire — plantera silencieusement).
En C#, pour gérer ces situations on utilise le bloc try-catch (ou, simplement, "piège à erreurs"). Il permet non seulement de "capturer" l'erreur, mais aussi de décider calmement quoi faire ensuite : afficher un message convivial, proposer de choisir un autre fichier, écrire l'erreur dans un log ou simplement retenter l'opération.
Dans les applications réelles
Si vous écrivez une calculatrice console, vous pouvez probablement vous passer de la gestion des erreurs liées aux fichiers. En revanche, si vous avez un programme de traitement de documents pour une entreprise ou un jeu qui enregistre la progression — ne pas gérer ces erreurs revient à marcher sur un câble sans filet : tôt ou tard quelque chose va se passer.
2. Petit rappel sur les exceptions
Syntaxe de base du try-catch
Avant d'aborder des cas concrets liés aux fichiers, rappelons la syntaxe de base de la construction try-catch (que nous avons déjà vue dans les leçons sur les exceptions) :
try
{
// Ici le code qui peut "lancer" une exception
}
catch (Exception ex)
{
// Ici on attrape toutes les exceptions possibles... Mais mieux vaut ne pas faire comme ça !
Console.WriteLine("Quelque chose s'est mal passé : " + ex.Message);
}
Dans le bloc try on écrit le code potentiellement dangereux, et dans le catch — ce qu'il faut faire si quelque chose tourne mal.
Blague de programmeur : "try-catch — c'est un peu l'équivalent numérique d'une cape d'invisibilité pour les erreurs. L'erreur est là, mais vous ne la voyez pas !"
Exceptions liées aux fichiers
Quand vous travaillez avec des fichiers via .NET, vous rencontrez souvent des exceptions — par exemple lors de la création d'un stream, de la lecture ou de l'écriture. Voici quelques scénarios typiques et les exceptions associées :
- Fichier introuvable → FileNotFoundException
- Pas d'accès au fichier/dossier → UnauthorizedAccessException
- Problèmes de chemin (par ex. caractères non autorisés, chemin trop long) → PathTooLongException, ArgumentException
- Fichier déjà utilisé par un autre processus → IOException
- Pas d'espace libre sur le disque → IOException
Le plus souvent, les opérations sur les fichiers sont liées à des classes dérivées de IOException. Toutes héritent du type de base System.Exception.
3. Pratique : attraper et gérer les erreurs lors du travail avec les fichiers
Examinons des cas typiques avec des exemples de notre "application en développement" : supposons qu'on essaie de lire un message de bienvenue depuis un fichier, de le traiter et de l'afficher à l'utilisateur. Nous ajouterons ici la gestion d'erreurs avec try-catch.
Exemple 1 : Gestion "fichier introuvable"
string filePath = "hello.txt";
try
{
string greeting = File.ReadAllText(filePath);
Console.WriteLine("Contenu du fichier : " + greeting);
}
catch (FileNotFoundException ex)
{
Console.WriteLine("Fichier introuvable ! Vérifiez que le fichier " + filePath + " existe.");
// On peut afficher des détails supplémentaires
Console.WriteLine("Détails techniques : " + ex.Message);
}
Si le fichier "hello.txt" est absent, le programme ne se terminera pas en erreur, mais affichera un message convivial. C'est aussi simple que ça pour rendre le programme résistant aux "erreurs bêtes" de l'utilisateur.
Exemple 2 : Violation des droits d'accès
Prenons un cas un peu plus compliqué : le fichier existe, mais nous n'avons pas les droits pour le lire (par exemple quelqu'un a donné seulement les droits d'écriture ou le fichier est dans un dossier protégé).
try
{
string secret = File.ReadAllText("C:\\Windows\\System32\\config.txt");
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine("Pas d'accès au fichier ! Essayez d'exécuter le programme en tant qu'administrateur.");
Console.WriteLine("Raison technique : " + ex.Message);
}
Exemple 3 : Exception générale IOException
Certaines erreurs peuvent être liées à des conflits de verrouillage (par ex. un autre processus garde le fichier ouvert), au manque d'espace disque ou à des problèmes matériels :
try
{
File.WriteAllText("important.txt", "Informations importantes !");
}
catch (IOException ex)
{
Console.WriteLine("Erreur lors de l'opération sur le fichier : probablement utilisé par une autre application ou espace disque insuffisant.");
Console.WriteLine("Raison technique : " + ex.Message);
}
4. Interception de plusieurs exceptions : gestion sélective
Parfois il faut réagir différemment selon le type d'exception. En .NET, il est possible d'indiquer plusieurs blocs catch — du plus spécifique au plus général (sinon le compilateur fera une grève bruyante à l'italienne contre votre code).
try
{
string content = File.ReadAllText("file.txt");
Console.WriteLine(content);
}
catch (FileNotFoundException ex)
{
Console.WriteLine("Fichier introuvable.");
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine("Pas d'accès au fichier !");
}
catch (IOException ex)
{
Console.WriteLine("Autre erreur d'E/S : " + ex.Message);
}
Important : si vous mettez catch (Exception ex) en premier, les autres blocs n'auront aucun sens, car le type de base attrapera tout !
5. try-catch imbriqués et tentatives répétées
Parfois vous voulez non seulement gérer l'erreur, mais aussi donner à l'utilisateur une chance de "se rattraper" — par exemple proposer de saisir le chemin d'un fichier existant :
string filePath;
string content;
int attempts = 0;
const int maxAttempts = 3;
do
{
Console.Write("Entrez le chemin du fichier : ");
filePath = Console.ReadLine();
try
{
content = File.ReadAllText(filePath);
Console.WriteLine("Contenu du fichier :\n" + content);
break;
}
catch (FileNotFoundException)
{
Console.WriteLine("Fichier introuvable ! Réessayez.");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Pas d'accès au fichier ! Essayez un autre fichier.");
}
attempts++;
}
while (attempts < maxAttempts);
if (attempts == maxAttempts)
Console.WriteLine("Trop de tentatives échouées.");
Ce code est une petite "interface amicale" pour un utilisateur qui a oublié où il a sauvegardé le fichier.
6. Erreurs courantes et particularités : de la paresse à la conscience
Une erreur très fréquente chez les débutants (et même chez les développeurs expérimentés) est d'attraper toutes les exceptions à la chaîne, d'écrire simplement catch (Exception) et d'afficher "Une erreur est survenue !", sans réfléchir aux causes. Cette approche est mauvaise pour plusieurs raisons. D'une part, elle masque des problèmes réels dans la logique métier de l'application. D'autre part, d'autres erreurs non liées aux fichiers (par ex. des fautes de frappe dans le code ou une erreur mathématique) peuvent être avalées par erreur, et la recherche de la cause réelle prendra énormément de temps.
Il est bien mieux d'attraper explicitement uniquement les erreurs que vous êtes capables de traiter de manière significative. Si vous ne savez pas quelle erreur s'est produite, il est préférable de la laisser "tomber" — c'est-à-dire de ne pas la capturer : laissez l'application planter, mais vous verrez la stack trace et comprendrez ce qu'il faut corriger.
Particularité : Certaines exceptions peuvent avoir des causes imbriquées (InnerException). Il est pratique de les analyser pour un diagnostic plus détaillé, surtout quand vous écrivez des journaux d'erreurs (logs).
Un autre point — si après un bloc catch la suite du programme est impossible (par ex. si l'ouverture du fichier de config principal a échoué), vous pouvez terminer l'exécution avec return ou même relancer l'exception (throw;) pour éviter de créer des "programmes zombies" avec des fonctionnalités à moitié opérationnelles.
GO TO FULL VERSION