1. Introduction : pourquoi les fichiers aiment "faire des caprices" ?
Ça arrive : vous ouvrez un document — et soudain le système affiche : "Fichier introuvable". Ou la tentative d'enregistrer se termine par : "Accès refusé". Ce sont précisément les cas où quelque chose de bizarre arrive avec les fichiers. Si avec les encodages on voit des "mojibakes", ici les problèmes sont liés au système de fichiers lui‑même.
On peut imaginer le système de fichiers comme une grande bibliothèque, et l'application comme le bibliothécaire. Quand elle demande un fichier, plusieurs réponses sont possibles :
- Le livre (c'est‑à‑dire le fichier) est absent — il n'existe tout simplement pas.
- La section de la bibliothèque où le livre devrait se trouver n'existe pas — le répertoire requis est manquant.
- Le livre est "verrouillé" ou emprunté à quelqu'un d'autre — le fichier est utilisé par un autre processus ou il n'y a pas les droits d'accès.
- L'étagère est pleine — pas assez d'espace disque pour créer un nouveau fichier.
- Ou, par exemple, on essaie de mettre un livre dans le rayon des retours DVD — c'est‑à‑dire une opération non supportée.
En C#, toutes ces situations se manifestent par des exceptions. Le boulot du développeur n'est pas de regarder le programme "crasher", mais d'anticiper les problèmes possibles et de les gérer proprement. Personne ne veut que l'utilisateur voit un message d'erreur cryptique rempli de texte incompréhensible.
Nous connaissons déjà la construction try-catch. C'est notre bouée de sauvetage qui permet d'"attraper" une exception et d'agir au lieu de laisser le programme se terminer brutalement.
// C'est notre vieil ami, rappel de la Leçon 57
try
{
// Ici on écrit le code qui peut provoquer une erreur
// Par exemple, essayer de lire un fichier
}
catch (Exception ex) // On attrape n'importe quelle exception
{
// Ici on gère l'erreur
Console.WriteLine($"Oups, une erreur est survenue : {ex.Message}");
}
Aujourd'hui, on va approfondir les exceptions spécifiques liées au travail avec les fichiers. Ça nous permettra d'écrire du code plus fiable, capable de "parler" au système de fichiers même quand il fait des caprices.
Les exceptions ne sont pas des bugs, ce sont des signaux de détresse !
Important : une exception n'est pas toujours une erreur dans votre code. Souvent c'est un signal que quelque chose s'est mal passé dans l'environnement externe avec lequel votre code interagit. Le système de fichiers est un bon exemple d'environnement externe. Vous pouvez écrire un code parfait pour lire un fichier, mais si l'utilisateur a supprimé ce fichier avant que votre programme n'ait eu le temps de le lire, vous obtiendrez une exception. Et c'est normal ! Votre tâche en tant que développeur est d'apprendre au programme à réagir dans ces situations.
Voyons les signaux de détresse les plus fréquents que vous pouvez rencontrer en travaillant avec des fichiers.
2. FileNotFoundException : le fichier n'existait pas
C'est probablement l'exception la plus courante quand on travaille avec des fichiers. Elle se produit quand on tente d'ouvrir, de lire ou d'opérer sur un fichier qui n'existe pas au chemin indiqué.
Exemple de la vie : vous demandez à un ami de vous apporter le livre "Programmation en C# 14 pour les nuls" depuis sa bibliothèque, et il répond : "Je n'ai pas ce livre". De la même manière, votre programme peut demander au système d'exploitation : "Donne‑moi le fichier settings.txt", et le système répondra : "Désolé, il n'existe pas".
Essayons d'écrire du code qui lit le fichier abracadabra.txt pour notre application de gestion de tâches. Si le fichier n'existe pas, on doit en informer l'utilisateur plutôt que de simplement "crasher".
try
{
using var reader = new StreamReader("abracadabra.txt");
Console.WriteLine(reader.ReadToEnd());
}
catch (FileNotFoundException ex)
{
Console.WriteLine("Fichier introuvable : " + ex.FileName);
}
Dans la vie de tous les jours, c'est comme arriver à un arrêt de bus et qu'il n'y ait pas de bus — ni de navette. Triste.
Note : Souvent cette exception est accompagnée d'un chemin incorrect (par exemple, vous avez oublié que vous travaillez depuis un autre répertoire).
3. DirectoryNotFoundException : les dossiers ont disparu
Cette exception ressemble beaucoup à FileNotFoundException, mais elle concerne le répertoire (le dossier) dans lequel le fichier devrait se trouver. Si vous indiquez un chemin, par exemple, "C:\MyDocuments\MyProject\Data\report.txt", et que le dossier Data n'existe pas, vous obtiendrez une DirectoryNotFoundException.
Dans notre application, si on voulait sauvegarder des paramètres dans un sous‑dossier data, par exemple "./data/app_settings.txt", et que ce dossier n'existe pas, une tentative d'écriture ou de lecture renverra ce problème.
DirectoryNotFoundException peut être attrapée séparément, comme FileNotFoundException, ou être une partie d'une IOException plus générale, dont nous parlerons plus loin.
try
{
using var writer = new StreamWriter(@"C:\very\strange\path\file.txt");
writer.WriteLine("Hello world");
}
catch (DirectoryNotFoundException ex)
{
Console.WriteLine("Répertoire introuvable !");
}
Erreur fréquente : Un dossier peut être supprimé à tout moment (par exemple, quelqu'un nettoie les fichiers temporaires), ou vous avez mal configuré le chemin d'écriture.
4. UnauthorizedAccessException : entrée interdite !
Imaginez que vous voulez mettre un livre sur une étagère où il y a un panneau "Accès réservé au personnel". C'est ça UnauthorizedAccessException ! Elle survient quand votre programme n'a pas les permissions nécessaires pour accéder à un fichier ou un répertoire. Ça peut être dû à :
- Droits insuffisants de l'utilisateur : Vous essayez d'écrire dans un dossier où seul un administrateur peut écrire (par exemple C:\Windows).
- Fichier marqué en lecture seule : Vous tentez de modifier un fichier ayant l'attribut "lecture seule".
- Fichier système ou caché : Avec des restrictions spécifiques.
C'est un problème très courant en environnement d'entreprise ou quand les utilisateurs installent des programmes dans des répertoires protégés.
Essayons d'écrire un fichier dans un dossier système où un utilisateur normal n'a vraisemblablement pas les droits. (Attention : exécutez ce code prudemment pour ne pas polluer des répertoires système, ou faites‑le en sandbox.)
try
{
using var writer = new StreamWriter("/system/settings.conf");
writer.WriteLine("Tous les pouvoirs aux étudiants !");
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine("Accès refusé au fichier ou au répertoire !");
}
Si vous exécutez ce code sans privilèges administrateur, vous verrez probablement le message "Accès à ce dossier refusé". Si vous l'exécutez en tant qu'administrateur, le fichier sera probablement créé. Cet exemple montre combien il est important de traiter UnauthorizedAccessException pour expliquer à l'utilisateur pourquoi l'opération a échoué.
5. IOException : le "ouf" universel du système de fichiers
IOException est l'exception la plus générale liée aux opérations d'entrée/sortie. Elle est levée lorsqu'il y a un problème avec le dispositif d'E/S ou le système de fichiers qui ne correspond pas à des exceptions plus spécifiques comme FileNotFoundException ou UnauthorizedAccessException.
Scénarios typiques où vous pouvez obtenir une IOException :
- Le fichier est déjà utilisé par un autre programme : Par exemple, vous essayez de supprimer un fichier ouvert dans Notepad ou dans un autre de vos programmes.
- Disque plein : Pas assez d'espace pour écrire le fichier.
- Fichier ou système de fichiers corrompu : Rare, mais possible.
- Problèmes réseau : Si le fichier est sur un disque réseau et que la connexion est coupée.
- Nom de fichier ou de répertoire trop long. (Ça peut être aussi un PathTooLongException, mais parfois ça remonte comme une IOException.)
IOException est un peu la "clé universelle" pour beaucoup de problèmes. Quand vous attrapez une IOException, regardez aussi sa propriété Message pour obtenir plus de détails sur ce qui s'est réellement passé.
try
{
using var file = new FileStream("busyfile.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.None);
// on garde le fichier ouvert par force
// ailleurs en même temps :
using var writer = new StreamWriter("busyfile.txt");
writer.WriteLine("Tentative d'écriture...");
}
catch (IOException ex)
{
Console.WriteLine("Erreur d'entrée/sortie : " + ex.Message);
}
Point important : IOException est la classe de base pour beaucoup d'autres exceptions liées aux fichiers.
6. D'autres "désagréments", mais tout aussi importants
PathTooLongException
C'est moins fréquent, mais ça arrive : votre chemin (ou le nom du fichier/répertoire) est trop long pour le système d'exploitation. Par exemple, si vous décidez d'inclure dans le nom de fichier un résumé complet de "Guerre et Paix", Windows ne vous pardonnera pas.
Sous Windows, la limite historique est de 260 caractères pour le chemin complet. Les versions récentes de l'OS et .NET permettent d'activer les "long paths", mais ce n'est pas toujours activé par défaut.
try
{
string veryLongPath = new string('a', 300); // 300 caractères !
using var writer = new StreamWriter(veryLongPath + ".txt");
writer.WriteLine("Ce nom de fichier est trop long !");
}
catch (PathTooLongException ex)
{
Console.WriteLine("Le nom du fichier ou le chemin est trop long !");
}
NotSupportedException
C'est un cas rare mais "surréaliste" : vous passez au constructeur de StreamReader ou FileStream une chaîne de chemin incorrecte, par exemple avec des caractères interdits, ou vous utilisez des chemins "magiques" du genre C:::\wow???\file.txt.
7. Subtilités utiles
Quelles exceptions correspondent à quoi
| Exception | Raison | Exemple de situation |
|---|---|---|
|
Fichier introuvable | Ouverture d'un fichier inexistant |
|
Répertoire introuvable | Ouverture d'un fichier dans un dossier supprimé |
|
Pas de droits (permissions) | Écriture dans un répertoire protégé |
|
Erreur générale d'E/S | Fichier utilisé par un autre processus |
|
Chemin trop long | Nom de fichier/dossier trop long |
|
Format de chemin incorrect | Chemin contenant des caractères interdits |
Erreurs fréquentes et cas spéciaux
Exemple : le fichier est occupé par un autre processus
Imaginez que vous, en vrai hacker, avez ouvert un fichier texte dans Notepad et oublié de le fermer. Pendant ce temps, votre programme tente d'écrire dans ce même fichier. Là, vous obtiendrez une IOException (ou même une "sharing violation").
Exemple : absence d'accès
Essayez de sauvegarder des données dans C:\Windows sans droits administrateur — vous obtiendrez UnauthorizedAccessException. Ça peut aussi arriver si vous avez ouvert un fichier en lecture seule et essayez d'y écrire.
Exemple : chemin incorrect
Sur Windows, on ne peut pas utiliser dans les noms de fichiers les caractères <>:"/\|?*. Si vous essayez, la routine lèvera NotSupportedException (ou ArgumentException).
Exemple : plus d'espace sur le disque
Étonnamment, cela génère aussi une IOException — par exemple quand le disque est plein (d'où l'intérêt de penser de temps en temps au dossier Downloads).
Comment éviter les pièges : bonnes pratiques de détection d'erreurs
- Vérifiez l'existence d'un fichier avec File.Exists et Directory.Exists avant d'essayer d'ouvrir le fichier. Mais attention : le fichier peut disparaître ou apparaître après la vérification (classique race condition).
- N'ignorez jamais complètement les exceptions (ne faites pas un simple catch { }), sauf si vous avez un logger strict. Au minimum, logguez ou affichez à l'utilisateur ce qui s'est passé.
- Essayez d'attraper les exceptions spécifiques (FileNotFoundException, DirectoryNotFoundException) plutôt que seulement l'Exception générique.
- Pour les apps cross‑platform, tenez compte des différences : permissions, formats de chemins, longueurs maximales — tout varie entre Windows, Linux et macOS.
- Pour les fichiers textes, spécifiez toujours explicitement l'encodage — sinon vous aurez des surprises.
- Pour les opérations bulk sur des fichiers, prévoyez un traitement par lots avec gestion des erreurs à chaque étape.
Fiche mémo des exceptions typiques
| Exception | Quand survient‑elle ? | Comment prévenir/traiter ? |
|---|---|---|
|
Fichier absent au chemin indiqué | Vérifier avec File.Exists ou créer le fichier |
|
Le chemin contient un répertoire inexistant | Vérifier le chemin, créer les répertoires avec Directory.CreateDirectory |
|
Pas de droits sur le fichier/le dossier, fichier en lecture seule, occupé par un autre processus | Lancer sous le bon utilisateur, vérifier l'ACL, fermer correctement les fichiers |
|
Erreur générale d'E/S, fichier occupé, disque plein | Utiliser try-catch, éviter de laisser des fichiers ouverts |
|
Chemin ou nom de fichier trop long | Raccourcir le chemin, utiliser des chemins relatifs |
|
Format de chemin incorrect | Vérifier la chaîne de chemin pour les caractères interdits |
Organigramme "Que faire en cas d'erreur sur un fichier ?"
flowchart TD
A[Opération sur fichier] --> B{Exception levée ?}
B -- Non --> C[Opération terminée avec succès]
B -- Oui --> D{Quel type d'exception ?}
D -- FileNotFound --> E[Demander à l'utilisateur de fournir le bon fichier ou de le créer]
D -- DirectoryNotFound --> F[Créer le répertoire manquant]
D -- UnauthorizedAccess --> G[Demander de relancer avec les bons droits]
D -- IOException --> H[Vérifier qui utilise le fichier, vérifier le disque]
D -- PathTooLong --> I[Raccourcir le chemin]
D -- NotSupported --> J[Vérifier le format du chemin]
D -- Other --> K[Afficher un message et revérifier les logs]
8. À quoi ressemblent les exceptions typiques dans une vraie application ?
En développant notre projet pédagogique, supposons que nous ayons une mini‑application qui sauvegarde les notes d'un utilisateur dans un fichier, puis les lit.
string notesPath = "notes.txt";
Console.Write("Entrez une note : ");
string note = Console.ReadLine();
try
{
// On sauvegarde la note
using var writer = new StreamWriter(notesPath, true, Encoding.UTF8);
writer.WriteLine(note);
// On lit toutes les notes
Console.WriteLine("Vos notes :");
using var reader = new StreamReader(notesPath, Encoding.UTF8);
Console.WriteLine(reader.ReadToEnd());
}
catch (FileNotFoundException)
{
Console.WriteLine("Fichier de notes introuvable. Essayez de le créer manuellement.");
}
catch (DirectoryNotFoundException)
{
Console.WriteLine("Le chemin vers le fichier de notes est incorrect. Vérifiez que le dossier existe.");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Pas de droits pour écrire ou lire le fichier. Lancez le programme en administrateur.");
}
catch (IOException ex)
{
Console.WriteLine("Une erreur d'entrée/sortie est survenue : " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("Erreur inattendue : " + ex.Message);
}
Dans cet exemple on montre une situation typique : on essaie de sauvegarder des données dans un fichier puis de les relire. Chaque bloc catch gère une classe d'erreurs — de l'absence du fichier ou du dossier aux problèmes de droits et aux erreurs générales d'E/S. Grâce à ça, le programme ne "plante" pas au premier pépin, mais informe l'utilisateur de ce qui s'est passé et de ce qu'il peut faire. Cette approche rend l'application plus fiable et conviviale.
GO TO FULL VERSION