CodeGym /Cours /C# SELF /Récupérer des infos sur les fichiers et les répertoires

Récupérer des infos sur les fichiers et les répertoires

C# SELF
Niveau 39 , Leçon 2
Disponible

1. Introduction

Sous Windows (et pas seulement) chaque fichier et répertoire possède un ensemble de propriétés (métadonnées) : chemin complet, nom, extension, taille, dates de création/modification/accès, attributs, etc. Imaginez : un fichier ce n'est pas que des octets, c'est aussi tout un profil qu'on peut lire avec FileInfo et DirectoryInfo.

Propriété Description Exemple
Chemin complet Nom complet du fichier/dossier
C:\data\myfile.txt
Nom Nom sans le chemin
myfile.txt
Extension .txt, .csv, .jpg, etc.
.txt
Taille (Bytes) Taille du fichier en octets
4096
Date de création Quand le fichier/dossier a été créé
2024-04-16 19:30:10
Date de modification Quand le contenu a été modifié pour la dernière fois
2024-05-01 18:14:02
Attributs Par exemple ReadOnly, Hidden, etc.
ReadOnly, Hidden
Dossier parent Le dossier qui contient le fichier/répertoire
C:\data

2. Travail approfondi sur les propriétés des fichiers

Analyse détaillée des timestamps

Les propriétés CreationTime, LastWriteTime, LastAccessTime retournent des DateTime et leur comportement dépend du filesystem et des opérations effectuées sur le fichier.


var fileInfo = new FileInfo("document.txt");

if (fileInfo.Exists)
{
    Console.WriteLine($"Créé : {fileInfo.CreationTime:yyyy-MM-dd HH:mm:ss}");
    Console.WriteLine($"Modifié : {fileInfo.LastWriteTime:yyyy-MM-dd HH:mm:ss}");
    Console.WriteLine($"Accédé : {fileInfo.LastAccessTime:yyyy-MM-dd HH:mm:ss}");
    
    // Différence entre creation et dernier changement
    var age = fileInfo.LastWriteTime - fileInfo.CreationTime;
    Console.WriteLine($"Le fichier a été modifié pendant environ : {age.TotalDays:F1} jours");
}

Intéressant : lors d'une copie la date de création est souvent mise à jour à la date courante, tandis que la date de modification peut être conservée depuis l'original. C'est important pour les sauvegardes et l'analyse d'activité.

Travail avec les extensions et les noms de fichiers

Les propriétés FullName, Name et Extension semblent simples, mais il y a des subtilités : absence d'extension, extensions composées comme .tar.gz et les fichiers cachés qui commencent par un point.


var files = new[]
{
    new FileInfo("document.txt"),
    new FileInfo("archive.tar.gz"),
    new FileInfo("README"),
    new FileInfo(".gitignore")
};

foreach (var file in files)
{
    Console.WriteLine($"Nom complet : {file.FullName}");
    Console.WriteLine($"Nom : {file.Name}");
    Console.WriteLine($"Extension : '{file.Extension}'");
    
    // Nom sans extension
    string nameWithoutExtension = Path.GetFileNameWithoutExtension(file.Name);
    Console.WriteLine($"Nom sans extension : {nameWithoutExtension}");
    Console.WriteLine("---");
}

Taille des fichiers et formatage

La propriété Length retourne la taille en octets ; pour l'utilisateur c'est plus pratique en Ko/Mo/Go. Fonction utilitaire :


static string FormatFileSize(long bytes)
{
    string[] suffixes = { "o", "Ko", "Mo", "Go", "To" };
    int counter = 0;
    decimal number = bytes;
    
    while (Math.Round(number / 1024) >= 1)
    {
        number /= 1024;
        counter++;
    }
    
    return $"{number:N1} {suffixes[counter]}";
}

// Utilisation
var file = new FileInfo("bigfile.zip");
if (file.Exists)
{
    Console.WriteLine($"Taille du fichier : {FormatFileSize(file.Length)}");
}

3. Travail avancé avec les attributs de fichiers

Les attributs sont représentés par l'enum FileAttributes (ensemble de flags bit à bit), donc un fichier peut avoir plusieurs attributs en même temps. On vérifie facilement avec HasFlag.


var fileInfo = new FileInfo("important.txt");

Console.WriteLine($"Attributs du fichier : {fileInfo.Attributes}");

// Vérifie des attributs spécifiques
if (fileInfo.Attributes.HasFlag(FileAttributes.Hidden))
{
    Console.WriteLine("Fichier caché !");
}

if (fileInfo.Attributes.HasFlag(FileAttributes.ReadOnly))
{
    Console.WriteLine("Fichier en lecture seule !");
}

if (fileInfo.Attributes.HasFlag(FileAttributes.System))
{
    Console.WriteLine("C'est un fichier système !");
}

On peut modifier les attributs par code :


// Rendre le fichier caché
fileInfo.Attributes |= FileAttributes.Hidden;

// Retirer l'attribut "ReadOnly"
fileInfo.Attributes &= ~FileAttributes.ReadOnly;

// Définir plusieurs attributs en une fois
fileInfo.Attributes = FileAttributes.ReadOnly | FileAttributes.Hidden;

4. Travail avancé avec les répertoires

Recherche de fichiers par pattern

Les méthodes GetFiles() et GetDirectories() acceptent des patterns et aident à filtrer le contenu.


var dir = new DirectoryInfo(@"C:\Projects");

if (dir.Exists)
{
    // Trouver tous les fichiers texte
    var textFiles = dir.GetFiles("*.txt");
    Console.WriteLine($"Fichiers texte trouvés : {textFiles.Length}");
    
    // Trouver tous les fichiers qui commencent par "temp"
    var tempFiles = dir.GetFiles("temp*");
    
    // Trouver tous les fichiers images
    var imageExtensions = new[] { "*.jpg", "*.png", "*.gif", "*.bmp" };
    var allImages = imageExtensions.SelectMany(ext => dir.GetFiles(ext)).ToArray();
    
    Console.WriteLine($"Images trouvées : {allImages.Length}");
}

Recherche récursive dans les sous-dossiers

Pour parcourir tous les sous-dossiers utilisez SearchOption.AllDirectories.


var dir = new DirectoryInfo(@"C:\Development");

// Trouver tous les fichiers C# dans tous les sous-dossiers
var csharpFiles = dir.GetFiles("*.cs", SearchOption.AllDirectories);
Console.WriteLine($"Total de fichiers .cs trouvés : {csharpFiles.Length}");

// Afficher les 10 premiers fichiers avec leurs chemins
foreach (var file in csharpFiles.Take(10))
{
    Console.WriteLine($"{file.FullName} ({FormatFileSize(file.Length)})");
}

Analyse du contenu d'un répertoire

Exemple d'analyse récapitulative : nombre de fichiers/dossiers, taille totale, répartition par extension et top-5 des plus gros fichiers.


static void AnalyzeDirectory(DirectoryInfo dir)
{
    if (!dir.Exists)
    {
        Console.WriteLine("Le dossier n'existe pas !");
        return;
    }
    
    var files = dir.GetFiles();
    var subdirs = dir.GetDirectories();
    
    Console.WriteLine($"Analyse du dossier : {dir.FullName}");
    Console.WriteLine($"Fichiers : {files.Length}, Sous-dossiers : {subdirs.Length}");
    
    if (files.Length == 0)
    {
        Console.WriteLine("Aucun fichier trouvé.");
        return;
    }
    
    long totalSize = files.Sum(f => f.Length);
    Console.WriteLine($"Taille totale des fichiers : {FormatFileSize(totalSize)}");
    
    // Groupement par extension
    var byExtension = files.GroupBy(f => f.Extension.ToLower())
                           .OrderByDescending(g => g.Sum(f => f.Length));
    
    Console.WriteLine("\nRépartition par type de fichier :");
    foreach (var group in byExtension)
    {
        string ext = string.IsNullOrEmpty(group.Key) ? "(sans extension)" : group.Key;
        long groupSize = group.Sum(f => f.Length);
        Console.WriteLine($"  {ext} : {group.Count()} fichiers, {FormatFileSize(groupSize)}");
    }
    
    // Top-5 des plus gros fichiers
    var largestFiles = files.OrderByDescending(f => f.Length).Take(5);
    Console.WriteLine("\nLes plus gros fichiers :");
    foreach (var file in largestFiles)
    {
        Console.WriteLine($"  {file.Name} : {FormatFileSize(file.Length)}");
    }
}

5. Calcul optimisé de la taille d'un dossier

Pour les gros dossiers, au lieu de GetFiles(), préférez les énumérateurs paresseux EnumerateFiles()/EnumerateDirectories(), gérez les exceptions et affichez éventuellement la progression.


static long GetDirectorySizeAdvanced(DirectoryInfo dir, bool showProgress = false)
{
    long totalSize = 0;
    int fileCount = 0;
    var inaccessibleDirs = new List<string>();
    
    try
    {
        // On utilise EnumerateFiles pour les gros dossiers (chargement paresseux)
        foreach (var file in dir.EnumerateFiles())
        {
            try
            {
                totalSize += file.Length;
                fileCount++;
                
                if (showProgress && fileCount % 1000 == 0)
                {
                    Console.WriteLine($"Fichiers traités : {fileCount}, taille : {FormatFileSize(totalSize)}");
                }
            }
            catch (UnauthorizedAccessException)
            {
                // Fichier inaccessible, on le saute
            }
            catch (IOException)
            {
                // Problème de lecture, on le saute
            }
        }
        
        // On traite récursivement les sous-dossiers
        foreach (var subdir in dir.EnumerateDirectories())
        {
            try
            {
                totalSize += GetDirectorySizeAdvanced(subdir, showProgress);
            }
            catch (UnauthorizedAccessException)
            {
                inaccessibleDirs.Add(subdir.FullName);
            }
        }
    }
    catch (UnauthorizedAccessException)
    {
        inaccessibleDirs.Add(dir.FullName);
    }
    
    if (inaccessibleDirs.Any() && showProgress)
    {
        Console.WriteLine($"Dossiers inaccessibles : {inaccessibleDirs.Count}");
    }
    
    return totalSize;
}

// Utilisation
var targetDir = new DirectoryInfo(@"C:\Users");
Console.WriteLine("On commence le calcul de la taille du dossier...");
long size = GetDirectorySizeAdvanced(targetDir, showProgress: true);
Console.WriteLine($"Taille totale : {FormatFileSize(size)}");

6. Applications pratiques des métadonnées

Recherche de fichiers par date

On recherche les fichiers modifiés dans une période donnée (pratique pour le nettoyage, l'analyse ou l'audit).


static void FindFilesByDate(DirectoryInfo dir, DateTime fromDate, DateTime toDate)
{
    Console.WriteLine($"Recherche des fichiers du {fromDate:yyyy-MM-dd} au {toDate:yyyy-MM-dd}");
    
    var matchingFiles = dir.GetFiles("*", SearchOption.AllDirectories)
                           .Where(f => f.LastWriteTime >= fromDate && f.LastWriteTime <= toDate)
                           .OrderByDescending(f => f.LastWriteTime);
    
    Console.WriteLine($"Fichiers trouvés : {matchingFiles.Count()}");
    
    foreach (var file in matchingFiles.Take(20))
    {
        Console.WriteLine($"{file.LastWriteTime:yyyy-MM-dd HH:mm} - {file.Name} ({FormatFileSize(file.Length)})");
    }
}

// Exemple : trouver tous les fichiers modifiés la semaine dernière
var dir = new DirectoryInfo(@"C:\Documents");
var weekAgo = DateTime.Now.AddDays(-7);
FindFilesByDate(dir, weekAgo, DateTime.Now);

Recherche de fichiers dupliqués

Méthode rapide — grouper par taille. Pour être sûr, ajouter le calcul d'un hash du contenu.


static void FindPotentialDuplicates(DirectoryInfo dir)
{
    Console.WriteLine($"Recherche de doublons potentiels dans {dir.FullName}");
    
    var files = dir.GetFiles("*", SearchOption.AllDirectories)
                   .Where(f => f.Length > 0) // On exclut les fichiers vides
                   .GroupBy(f => f.Length)
                   .Where(g => g.Count() > 1) // Seulement les groupes avec plusieurs fichiers
                   .OrderByDescending(g => g.Key); // Tri par taille
    
    foreach (var sizeGroup in files.Take(10))
    {
        Console.WriteLine($"\nFichiers de taille {FormatFileSize(sizeGroup.Key)} ({sizeGroup.Count()} fichiers) :");
        foreach (var file in sizeGroup)
        {
            Console.WriteLine($"  {file.FullName}");
            Console.WriteLine($"    Modifié : {file.LastWriteTime:yyyy-MM-dd HH:mm:ss}");
        }
    }
}

Surveillance des changements dans un dossier

On affiche les fichiers modifiés depuis les N dernières minutes.


static void MonitorRecentChanges(DirectoryInfo dir, int minutesBack = 60)
{
    var cutoffTime = DateTime.Now.AddMinutes(-minutesBack);
    
    var recentFiles = dir.GetFiles("*", SearchOption.AllDirectories)
                         .Where(f => f.LastWriteTime > cutoffTime)
                         .OrderByDescending(f => f.LastWriteTime);
    
    Console.WriteLine($"Fichiers modifiés au cours des {minutesBack} dernières minutes :");
    
    if (!recentFiles.Any())
    {
        Console.WriteLine("Aucun changement trouvé.");
        return;
    }
    
    foreach (var file in recentFiles)
    {
        var minutesAgo = (DateTime.Now - file.LastWriteTime).TotalMinutes;
        Console.WriteLine($"{file.Name} - il y a {minutesAgo:F0} min. ({FormatFileSize(file.Length)})");
    }
}

7. Travail avec les répertoires parents

La propriété Directory sur FileInfo et Parent sur DirectoryInfo permettent de remonter dans la hiérarchie.


var file = new FileInfo(@"C:\Projects\MyApp\src\Program.cs");

Console.WriteLine($"Fichier : {file.Name}");
Console.WriteLine($"Dossier : {file.Directory.Name}");
Console.WriteLine($"Dossier parent : {file.Directory.Parent.Name}");
Console.WriteLine($"Dossier racine du projet : {file.Directory.Parent.Parent.Name}");

// On peut remonter jusqu'à la racine
var currentDir = file.Directory;
while (currentDir.Parent != null)
{
    Console.WriteLine($"Niveau : {currentDir.Name}");
    currentDir = currentDir.Parent;
}
Console.WriteLine($"Racine : {currentDir.Name}");

8. Pièges et erreurs typiques

1. Mise en cache. Les objets FileInfo et DirectoryInfo mettent en cache les valeurs. Si l'objet a été modifié après sa création, les données peuvent être périmées. Utilisez Refresh() pour actualiser.


var file = new FileInfo("test.txt");
file.Refresh(); // Actualise les métadonnées

2. Exceptions d'accès. Certains fichiers et dossiers sont inaccessibles : gérez UnauthorizedAccessException et d'autres erreurs d'accès.


try
{
    var files = new DirectoryInfo(path).GetFiles();
}
catch (UnauthorizedAccessException)
{
    Console.WriteLine("Accès refusé");
}

3. Timestamps. Lors d'une copie CreationTime peut changer, alors que LastWriteTime peut rester. Ça impacte les rapports et les algos de synchronisation.


File.Copy("a.txt", "b.txt");

4. Performance. GetFiles() charge tout en mémoire et peut ralentir sur les gros dossiers. Préférez EnumerateFiles() pour une énumération paresseuse.

2
Mission
C# SELF, niveau 39, leçon 2
Bloqué
Parcourir le contenu d’un répertoire
Parcourir le contenu d’un répertoire
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION