Gewusst wie: Durchlaufen einer Verzeichnisstruktur (C#-Programmierhandbuch)How to: Iterate Through a Directory Tree (C# Programming Guide)

Der Ausdruck „Durchlaufen einer Verzeichnisstruktur“ bedeutet, dass auf jede Datei in jedem verschachtelten Unterverzeichnis in einem angegebenen Stammordner in einer beliebigen Tiefe zugegriffen wird.The phrase "iterate a directory tree" means to access each file in each nested subdirectory under a specified root folder, to any depth. Sie müssen nicht unbedingt jede Datei öffnen.You do not necessarily have to open each file. Sie können einfach den Namen der Datei oder dem Unterverzeichnis als string abrufen, oder Sie können zusätzliche Informationen eines System.IO.FileInfo oder System.IO.DirectoryInfo-Objekts abrufen.You can just retrieve the name of the file or subdirectory as a string, or you can retrieve additional information in the form of a System.IO.FileInfo or System.IO.DirectoryInfo object.

Hinweis

In Windows sind die Begriffe „Verzeichnis“ und „Ordner“ austauschbar.In Windows, the terms "directory" and "folder" are used interchangeably. Die meisten Dokumentationen und der Text der Benutzeroberfläche verwenden den Begriff „Ordner“, aber die .NET Framework.NET Framework-Klassenbibliothek verwendet den Begriff „Verzeichnis“.Most documentation and user interface text uses the term "folder," but the .NET Framework.NET Framework class library uses the term "directory."

Im einfachsten Fall, in dem Sie ganz sicher sind, dass Sie über die Zugriffsberechtigungen für alle Verzeichnisse in einem angegebenen Stamm verfügen, können Sie das System.IO.SearchOption.AllDirectories-Flag verwenden.In the simplest case, in which you know for certain that you have access permissions for all directories under a specified root, you can use the System.IO.SearchOption.AllDirectories flag. Dieses Flag gibt alle geschachtelten Unterverzeichnisse zurück, die mit dem angegebenen Muster übereinstimmen.This flag returns all the nested subdirectories that match the specified pattern. Im folgenden Beispiel wird die Verwendung dieses Flags veranschaulicht.The following example shows how to use this flag.

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

Der Schwachpunkt bei diesem Ansatz ist, dass die Methode fehlschlägt und keine Verzeichnisse zurückgibt, wenn eines der Unterverzeichnisse im angegebenen Stamm eine DirectoryNotFoundException oder UnauthorizedAccessException bewirkt.The weakness in this approach is that if any one of the subdirectories under the specified root causes a DirectoryNotFoundException or UnauthorizedAccessException, the whole method fails and returns no directories. Dasselbe gilt bei Verwendung der GetFiles-Methode.The same is true when you use the GetFiles method. Wenn Sie diese Ausnahmen für bestimmte Unterverzeichnisse behandeln müssen, müssen Sie die Verzeichnisstruktur manuell durchlaufen, wie in den folgenden Beispielen gezeigt.If you have to handle these exceptions on specific subfolders, you must manually walk the directory tree, as shown in the following examples.

Wenn Sie eine Verzeichnisstruktur manuell durchlaufen, können Sie zuerst die Unterverzeichnisse (Durchlauf vor der Sortierung) oder die Dateien (Durchlauf nach der Sortierung) behandeln.When you manually walk a directory tree, you can handle the subdirectories first (pre-order traversal), or the files first (post-order traversal). Wenn Sie einen Durchlauf vor der Sortierung ausführen, durchlaufen Sie die gesamte Struktur unter dem aktuellen Ordner, bevor Sie die Dateien durchlaufen, die sich direkt in diesem Ordner befinden.If you perform a pre-order traversal, you walk the whole tree under the current folder before iterating through the files that are directly in that folder itself. In den Beispielen weiter unten in diesem Dokument wird ein Durchlauf nach der Sortierung ausgeführt, aber Sie können die Beispiele problemlos abändern, um einen Durchlauf vor der Sortierung auszuführen.The examples later in this document perform post-order traversal, but you can easily modify them to perform pre-order traversal.

Eine andere Option ist, entweder Rekursion oder einen stapelbasierten Durchlauf zu verwenden.Another option is whether to use recursion or a stack-based traversal. In den Beispielen weiter unten in diesem Dokument werden beide Ansätze gezeigt.The examples later in this document show both approaches.

Wenn Sie mehrere Vorgänge für Dateien und Ordner ausführen müssen, können Sie diese Beispiele modularisieren, indem Sie den Vorgang in separate Funktionen umgestalten, die Sie mit einem einzelnen Delegaten aufrufen können.If you have to perform a variety of operations on files and folders, you can modularize these examples by refactoring the operation into separate functions that you can invoke by using a single delegate.

Hinweis

NTFS-Dateisysteme können Analysepunkte in Form von Verknüpfungspunkten, symbolischen Verknüpfungen und festen Links enthalten.NTFS file systems can contain reparse points in the form of junction points, symbolic links, and hard links. .NET Framework-Methoden wie z.B. GetFiles und GetDirectories geben keine Unterverzeichnisse unter einem Analysepunkt zurück.The .NET Framework methods such as GetFiles and GetDirectories will not return any subdirectories under a reparse point. Dieses Verhalten schützt vor dem Risiko einer Endlosschleife, wenn zwei Analysepunkte aufeinander verweisen.This behavior guards against the risk of entering into an infinite loop when two reparse points refer to each other. Im Allgemeinen sollten Sie bei Analysepunkten äußerst vorsichtig sein, um sicherzustellen, dass Sie nicht versehentlich Dateien ändern oder löschen.In general, you should use extreme caution when you deal with reparse points to ensure that you do not unintentionally modify or delete files. Wenn Sie genaue Kontrolle über Analysepunkte benötigen, verwenden Sie einen Plattformaufruf oder nativen Code, um die entsprechenden Win32-Dateisystemmethoden direkt aufzurufen.If you require precise control over reparse points, use platform invoke or native code to call the appropriate Win32 file system methods directly.

BeispielExample

Das folgende Beispiel zeigt, wie Sie eine Verzeichnisstruktur mit Rekursion direkt durchlaufen.The following example shows how to walk a directory tree by using recursion. Der rekursive Ansatz ist elegant, kann aber eine Stapelüberlaufausnahme verursachen, wenn die Verzeichnisstruktur groß und tief verschachtelt ist.The recursive approach is elegant but has the potential to cause a stack overflow exception if the directory tree is large and deeply nested.

Die besonderen Ausnahmen, die verarbeitet werden, und die besonderen Aktionen, die für jede Datei und jeden Ordner ausgeführt werden, werden nur als Beispiele angegeben.The particular exceptions that are handled, and the particular actions that are performed on each file or folder, are provided as examples only. Sie sollten diesen Code für Ihre speziellen Anforderungen ändern.You should modify this code to meet your specific requirements. Weitere Informationen finden Sie in den Kommentaren im Code.See the comments in the code for more information.

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

BeispielExample

Im folgenden Beispiel wird gezeigt, wie Sie Dateien und Ordner in einer Verzeichnisstruktur ohne Rekursion durchlaufen.The following example shows how to iterate through files and folders in a directory tree without using recursion. Diese Technik verwendet den generischen Stack<T>-Auflistungstyp, bei dem es sich um einen LIFO-Stapel handelt.This technique uses the generic Stack<T> collection type, which is a last in first out (LIFO) stack.

Die besonderen Ausnahmen, die verarbeitet werden, und die besonderen Aktionen, die für jede Datei und jeden Ordner ausgeführt werden, werden nur als Beispiele angegeben.The particular exceptions that are handled, and the particular actions that are performed on each file or folder, are provided as examples only. Sie sollten diesen Code für Ihre speziellen Anforderungen ändern.You should modify this code to meet your specific requirements. Weitere Informationen finden Sie in den Kommentaren im Code.See the comments in the code for more information.

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

Es ist im Allgemeinen zu zeitaufwändig, bei allen Ordnern zu testen, ob die Anwendung über die Berechtigung zum Öffnen verfügt.It is generally too time-consuming to test every folder to determine whether your application has permission to open it. Im Codebeispiel wird daher nur dieser Teil der Operation in einen try/catch-Block eingeschlossen.Therefore, the code example just encloses that part of the operation in a try/catch block. Sie können den catch-Block ändern, sodass Sie bei verweigertem Zugriff auf einen Ordner versuchen, Ihre Berechtigungen zu erhöhen, und dann erneut darauf zugreifen.You can modify the catch block so that when you are denied access to a folder, you try to elevate your permissions and then access it again. In der Regel sollten Sie nur die Ausnahmen abfangen, die Sie behandeln können, ohne Ihre Anwendung in einem unbekannten Status zu lassen.As a rule, only catch those exceptions that you can handle without leaving your application in an unknown state.

Wenn Sie den Inhalt einer Verzeichnisstruktur entweder im Arbeitsspeicher oder auf dem Datenträger speichern müssen, speichern Sie am besten nur die FullName-Eigenschaft (vom Typ string) für jede Datei.If you must store the contents of a directory tree, either in memory or on disk, the best option is to store only the FullName property (of type string) for each file. Anschließend können Sie diese Zeichenfolge nach Bedarf zum Erstellen eines neuen FileInfo- oder DirectoryInfo-Objekts verwenden, oder eine beliebige Datei öffnen, für die zusätzliche Verarbeitung erforderlich ist.You can then use this string to create a new FileInfo or DirectoryInfo object as necessary, or open any file that requires additional processing.

Stabile ProgrammierungRobust Programming

Bei stabilem Dateiiterationscode müssen viele komplexe Zusammenhänge des Dateisystems berücksichtigt werden.Robust file iteration code must take into account many complexities of the file system. Weitere Informationen finden Sie unter NTFS Technical Reference (Technische Referenz für NTFS).For more information, see NTFS Technical Reference.

Siehe auchSee Also

System.IO
LINQ und DateiverzeichnisseLINQ and File Directories
Das Dateisystem und die Registrierung (C#-Programmierhandbuch)File System and the Registry (C# Programming Guide)