Partager via


Comment : itérer au sein d'une arborescence de répertoires (Guide de programmation C#)

L'expression « itérer au sein d'une arborescence de répertoires » signifie accéder à chaque fichier des différents sous-répertoires imbriqués d'un dossier racine spécifié, quel que soit le nombre de niveaux.Vous ne devez pas nécessairement ouvrir chaque fichier.Vous pouvez simplement récupérer le nom du fichier ou du sous-répertoire en tant que string ou vous pouvez récupérer des informations supplémentaires sous la forme d'un objet System.IO.FileInfo ou System.IO.DirectoryInfo.

[!REMARQUE]

Dans Windows, les termes « répertoire » et « dossier » sont utilisés indifféremment.Le texte de la documentation et de l'interface utilisateur utilise généralement le terme « dossier », mais la bibliothèque de classes .NET Framework utilise le terme « répertoire ».

Dans le cas le plus simple, à savoir quand vous êtes sûr que vous disposez d'autorisations d'accès pour tous les répertoires situés sous une racine spécifiée, vous pouvez utiliser l'indicateur System.IO.SearchOption.AllDirectories.Cet indicateur retourne tous les sous-répertoires imbriqués qui correspondent au modèle spécifié.L'exemple suivant montre comment utiliser cet indicateur.

root.GetDirectories("*.*", System.IO.SearchOption.AllDirectories);

La faiblesse de cette approche réside dans le fait que si l'un des sous-répertoires situés sous la racine spécifiée entraîne une DirectoryNotFoundException ou une UnauthorizedAccessException, la méthode entière échoue et ne retourne pas de répertoires.Il en va de même lorsque vous utilisez la méthode GetFiles.Si vous devez gérer ces exceptions pour des sous-dossiers spécifiques, vous devez parcourir l'arborescence de répertoires manuellement, comme l'illustrent les exemples suivants.

Lorsque vous parcourez une arborescence de répertoires manuellement, vous pouvez gérer les sous-répertoires en premier (balayage pré-ordre) ou les fichiers en premier (balayage post-ordre).Si vous effectuez un balayage pré-ordre, vous parcourez l'arborescence entière sous le dossier actif avant d'itérer au sein des fichiers qui sont situés directement dans ce dossier.Les exemples présentés ultérieurement dans ce document effectuent un balayage post-ordre, mais vous pouvez les modifier facilement pour effectuer un balayage pré-ordre.

Une autre option concerne l'utilisation de la récurrence ou d'un balayage de type pile.Les exemples présentés ultérieurement dans ce document montrent les deux approches.

Si vous devez effectuer diverses opérations sur des fichiers et des dossiers, vous pouvez organiser ces exemples par modules en refactorisant l'opération en fonctions séparées que vous pouvez appeler en utilisant un délégué unique.

[!REMARQUE]

Les systèmes de fichiers NTFS peuvent contenir des points d'analyse sous la forme de points de jonction, de liens physiques et de liens réels.Les méthodes .NET Framework telles que GetFiles et GetDirectories ne retournent pas de sous-répertoires situés sous un point d'analyse.Ce comportement empêche d'entrer dans une boucle infinie lorsque deux points d'analyse font référence l'un à l'autre.En général, soyez extrêmement prudent lorsque vous utilisez des points d'analyse : vous risquez de modifier ou de supprimer des fichiers involontairement.Si vous avez besoin d'un contrôle précis sur les points d'analyse, utilisez l'appel de code non managé ou le code natif pour appeler directement les méthodes de système de fichiers Win32 appropriées.

Exemple

L'exemple suivant montre comment parcourir une arborescence de répertoires à l'aide de la récurrence.L'approche récursive est élégante, mais est susceptible de provoquer une exception de dépassement de capacité de la pile si l'arborescence de répertoires est importante et comporte un grand nombre de niveaux d'imbrication.

Les exceptions particulières gérées et les actions particulières effectuées sur chaque fichier ou dossier sont fournies uniquement à titre d'exemples.Vous devez modifier ce code en fonction de vos besoins.Pour plus d'informations, consultez les commentaires du code.

public class RecursiveFileSearch
{
    static System.Collections.Specialized.StringCollection log = new System.Collections.Specialized.StringCollection();

    static void Main()
    {
        // Start with drives if you have to search the entire computer.
        string[] drives = System.Environment.GetLogicalDrives();

        foreach (string dr in drives)
        {
            System.IO.DriveInfo di = new System.IO.DriveInfo(dr);

            // Here we skip the drive if it is not ready to be read. This
            // is not necessarily the appropriate action in all scenarios.
            if (!di.IsReady)
            {
                Console.WriteLine("The drive {0} could not be read", di.Name);
                continue;
            }
            System.IO.DirectoryInfo rootDir = di.RootDirectory;
            WalkDirectoryTree(rootDir);
        }

        // Write out all the files that could not be processed.
        Console.WriteLine("Files with restricted access:");
        foreach (string s in log)
        {
            Console.WriteLine(s);
        }
        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key");
        Console.ReadKey();
    }

    static void WalkDirectoryTree(System.IO.DirectoryInfo root)
    {
        System.IO.FileInfo[] files = null;
        System.IO.DirectoryInfo[] subDirs = null;

        // First, process all the files directly under this folder
        try
        {
            files = root.GetFiles("*.*");
        }
        // This is thrown if even one of the files requires permissions greater
        // than the application provides.
        catch (UnauthorizedAccessException e)
        {
            // This code just writes out the message and continues to recurse.
            // You may decide to do something different here. For example, you
            // can try to elevate your privileges and access the file again.
            log.Add(e.Message);
        }

        catch (System.IO.DirectoryNotFoundException e)
        {
            Console.WriteLine(e.Message);
        }

        if (files != null)
        {
            foreach (System.IO.FileInfo fi in files)
            {
                // In this example, we only access the existing FileInfo object. If we
                // want to open, delete or modify the file, then
                // a try-catch block is required here to handle the case
                // where the file has been deleted since the call to TraverseTree().
                Console.WriteLine(fi.FullName);
            }

            // Now find all the subdirectories under this directory.
            subDirs = root.GetDirectories();

            foreach (System.IO.DirectoryInfo dirInfo in subDirs)
            {
                // Resursive call for each subdirectory.
                WalkDirectoryTree(dirInfo);
            }
        }            
    }
}

L'exemple suivant indique comment itérer au sein de fichiers et de dossiers dans une arborescence de répertoires sans utiliser la récurrence.Cette technique utilise le type de collection générique Stack<T>, à savoir une pile de type dernier entré, premier sorti (LIFO, Last-In-First-Out).

Les exceptions particulières gérées et les actions particulières effectuées sur chaque fichier ou dossier sont fournies uniquement à titre d'exemples.Vous devez modifier ce code en fonction de vos besoins.Pour plus d'informations, consultez les commentaires du code.

public class StackBasedIteration
{
    static void Main(string[] args)
    {
        // Specify the starting folder on the command line, or in 
        // Visual Studio in the Project > Properties > Debug pane.
        TraverseTree(args[0]);

        Console.WriteLine("Press any key");
        Console.ReadKey();
    }

    public static void TraverseTree(string root)
    {
        // Data structure to hold names of subfolders to be
        // examined for files.
        Stack<string> dirs = new Stack<string>(20);

        if (!System.IO.Directory.Exists(root))
        {
            throw new ArgumentException();
        }
        dirs.Push(root);

        while (dirs.Count > 0)
        {
            string currentDir = dirs.Pop();
            string[] subDirs;
            try
            {
                subDirs = System.IO.Directory.GetDirectories(currentDir);
            }
            // An UnauthorizedAccessException exception will be thrown if we do not have
            // discovery permission on a folder or file. It may or may not be acceptable 
            // to ignore the exception and continue enumerating the remaining files and 
            // folders. It is also possible (but unlikely) that a DirectoryNotFound exception 
            // will be raised. This will happen if currentDir has been deleted by
            // another application or thread after our call to Directory.Exists. The 
            // choice of which exceptions to catch depends entirely on the specific task 
            // you are intending to perform and also on how much you know with certainty 
            // about the systems on which this code will run.
            catch (UnauthorizedAccessException e)
            {                    
                Console.WriteLine(e.Message);
                continue;
            }
            catch (System.IO.DirectoryNotFoundException e)
            {
                Console.WriteLine(e.Message);
                continue;
            }

            string[] files = null;
            try
            {
                files = System.IO.Directory.GetFiles(currentDir);
            }

            catch (UnauthorizedAccessException e)
            {

                Console.WriteLine(e.Message);
                continue;
            }

            catch (System.IO.DirectoryNotFoundException e)
            {
                Console.WriteLine(e.Message);
                continue;
            }
            // Perform the required action on each file here.
            // Modify this block to perform your required task.
            foreach (string file in files)
            {
                try
                {
                    // Perform whatever action is required in your scenario.
                    System.IO.FileInfo fi = new System.IO.FileInfo(file);
                    Console.WriteLine("{0}: {1}, {2}", fi.Name, fi.Length, fi.CreationTime);
                }
                catch (System.IO.FileNotFoundException e)
                {
                    // If file was deleted by a separate application
                    //  or thread since the call to TraverseTree()
                    // then just continue.
                    Console.WriteLine(e.Message);
                    continue;
                }
            }

            // Push the subdirectories onto the stack for traversal.
            // This could also be done before handing the files.
            foreach (string str in subDirs)
                dirs.Push(str);
        }
    }
}

Il est généralement trop long de tester chaque dossier afin de déterminer si votre application a l'autorisation de l'ouvrir.Par conséquent, l'exemple de code englobe simplement cette partie de l'opération dans un bloc try/catch.Vous pouvez modifier le bloc catch afin que, lorsque l'accès à un dossier vous est refusé, vous essayiez d'élever vos autorisations et d'y accéder de nouveau.En règle générale, interceptez uniquement les exceptions que vous pouvez gérer sans laisser votre application dans un état inconnu.

Si vous devez stocker le contenu d'une arborescence de répertoires en mémoire ou sur le disque, la meilleure option est de stocker uniquement la propriété FullName (de type string) pour chaque fichier.Vous pouvez utiliser ensuite cette chaîne pour créer au besoin un nouvel objet FileInfo ou DirectoryInfo ou pour ouvrir les fichiers qui requièrent un traitement supplémentaire.

Programmation fiable

Pour être fiables, les codes permettant d'itérer au sein des fichiers doivent prendre en considération les nombreuses complexités du système de fichiers.Pour plus d'informations, consultez NTFS Technical Reference.

Voir aussi

Référence

System.IO

Concepts

LINQ et répertoires de fichiers

Autres ressources

Système de fichiers et Registre (Guide de programmation C#)