Partilhar via


Como: iterar por meio de uma árvore de diretórios (guia de programação do C#)

A frase "iterar uma árvore de diretório" significa acessar cada arquivo em cada subdiretório aninhado em uma pasta raiz especificada, a qualquer profundidade.Você não tem necessariamente abrir cada arquivo.Você pode recuperar apenas o nome do arquivo ou subdiretório como um string, ou você pode recuperar informações adicionais na forma de um System.IO.FileInfo ou System.IO.DirectoryInfo objeto.

ObservaçãoObservação

No Windows, os termos "diretório" e "pasta" são intercambiáveis.A maioria dos textos de interface de usuário e documentação usa o termo "pasta", mas o .NET Framework biblioteca de classe usa o termo "diretório".

No caso mais simples, em que você sabe com certeza que você tenha permissões de acesso para todos os diretórios em uma raiz especificado, você pode usar o System.IO.SearchOption.AllDirectories sinalizador.Esse sinalizador retorna todas as subpastas aninhadas que correspondem ao padrão especificado.O exemplo a seguir mostra como usar esse sinalizador.

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

O ponto fraco nesta abordagem é que, se qualquer um dos subdiretórios abaixo da raiz especificado faz com que uma DirectoryNotFoundException ou UnauthorizedAccessException, o método inteiro falhará e não retornará nenhum diretório.O mesmo acontece quando você usa o GetFiles método.Se você tiver que manipular essas exceções em subpastas específicas, você deve movimentar manualmente a árvore de diretório, como mostrado nos exemplos a seguir.

Quando você movimentar manualmente uma árvore de diretório, você pode manipular os subdiretórios primeiro (o percurso de pre-order), ou os arquivos primeiro (traversal Pre).Se você realizar um percurso de pre-order, percorrer toda a árvore sob a pasta atual antes de iteração os arquivos que estão diretamente nessa pasta propriamente dita.Os exemplos mais adiante neste documento realizar traversal Pre, mas você pode facilmente modificá-los para realizar o percurso de pre-order.

Outra opção é usar a recursão ou uma passagem baseado em pilha.Os exemplos mais adiante neste documento mostram as duas abordagens.

Se você tiver que realizar uma variedade de operações em arquivos e pastas, você pode modularizar esses exemplos por refatoração a operação em funções separadas que você pode chamar usando um único delegate.

ObservaçãoObservação

Sistemas de arquivos NTFS podem conter pontos de nova análise na forma de pontos de junção, links simbólicos, e links físicos.A.Métodos de NET Framework, como GetFiles e GetDirectories não retornará todas as subpastas sob um ponto de nova análise.Esse comportamento protege contra o risco de celebrar um loop infinito quando dois pontos de nova análise fazer referência entre si.Em geral, você deve usar muito cuidado ao lidar com pontos de nova análise para garantir que não, inadvertidamente, modificar ou excluir arquivos.Se você precisar de um controle preciso sobre pontos de nova análise, use invocação de plataforma ou código nativo para chamar métodos do sistema o arquivo apropriado do Win32 diretamente.

Exemplo

O exemplo a seguir mostra como percorrer uma árvore de diretórios usando recursão.A abordagem recursiva é elegante, mas tem o potencial de causar uma exceção de estouro de pilha, se a árvore de diretórios é grande e profundamente aninhados.

As exceções específicas que são manipuladas e determinadas ações que são executadas em cada arquivo ou pasta, são fornecidas apenas como exemplo.Você deve modificar este código para atender às suas necessidades específicas.Consulte os comentários no código para obter mais informações.

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);
            }
        }            
    }
}

O exemplo a seguir mostra como iterar por meio de arquivos e pastas em uma árvore de diretório sem o uso de recursão.Esta técnica usa a genérica Stack<T> tipo de coleção, que é um último na pilha primeira a sair (LIFO).

As exceções específicas que são manipuladas e determinadas ações que são executadas em cada arquivo ou pasta, são fornecidas apenas como exemplo.Você deve modificar este código para atender às suas necessidades específicas.Consulte os comentários no código para obter mais informações.

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);
        }
    }
}

Geralmente é muito demorado testar todas as pastas para determinar se o seu aplicativo tem permissão para abri-lo.Portanto, o exemplo de código inclui apenas essa parte da operação em um try/catch bloco.Você pode modificar o catch bloquear para que quando são negado o acesso a uma pasta, você tente elevar suas permissões e, em seguida, acessá-lo novamente.Como regra, apenas captura essas exceções que você pode manipular sem sair do seu aplicativo em um estado desconhecido.

Se você deve armazenar o conteúdo de uma árvore de diretório, na memória ou no disco, a melhor opção é armazenar somente a FullName propriedade (do tipo string) para cada arquivo.Você pode usar essa seqüência de caracteres para criar um novo FileInfo ou DirectoryInfo de objeto conforme necessário, ou abrir qualquer arquivo que requer processamento adicional.

Programação robusta

Código de iteração de arquivo robusto deve levar em conta muitos complexidades do sistema de arquivos.Para obter mais informações, consulte Referência técnica do NTFS.

Consulte também

Referência

System.IO

Conceitos

LINQ e diretórios de arquivos

Outros recursos

Sistema de arquivos e registro (guia de programação do C#)