使用遞迴獲取目錄樹

遞迴的一個用途是導航分層資料結構,如檔案系統目錄樹,而不知道樹有多少級別或每個級別上的物件數。在此示例中,你將看到如何在目錄樹上使用遞迴來查詢指定目錄的所有子目錄,並將整個樹列印到控制檯。

internal class Program
{
    internal const int RootLevel = 0;
    internal const char Tab = '\t';

    internal static void Main()
    {
        Console.WriteLine("Enter the path of the root directory:");
        var rootDirectorypath = Console.ReadLine();

        Console.WriteLine(
            $"Getting directory tree of '{rootDirectorypath}'");

        PrintDirectoryTree(rootDirectorypath);
        Console.WriteLine("Press 'Enter' to quit...");
        Console.ReadLine();
    }

    internal static void PrintDirectoryTree(string rootDirectoryPath)
    {
        try
        {
            if (!Directory.Exists(rootDirectoryPath))
            {
                throw new DirectoryNotFoundException(
                    $"Directory '{rootDirectoryPath}' not found.");
            }

            var rootDirectory = new DirectoryInfo(rootDirectoryPath);
            PrintDirectoryTree(rootDirectory, RootLevel);
        }
        catch (DirectoryNotFoundException e)
        {
            Console.WriteLine(e.Message);
        }
    }

    private static void PrintDirectoryTree(
        DirectoryInfo directory, int currentLevel)
    {
        var indentation = string.Empty;
        for (var i = RootLevel; i < currentLevel; i++)
        {
            indentation += Tab;
        }

        Console.WriteLine($"{indentation}-{directory.Name}");
        var nextLevel = currentLevel + 1;
        try
        {
            foreach (var subDirectory in directory.GetDirectories())
            {
                PrintDirectoryTree(subDirectory, nextLevel);
            }
        }
        catch (UnauthorizedAccessException e)
        {
            Console.WriteLine($"{indentation}-{e.Message}");
        }
    }
}

此程式碼比完成此任務的最低限度更復雜,因為它包括異常檢查以處理獲取目錄的任何問題。你可以在下面找到將程式碼拆分為較小的段,並對每個段進行說明。

Main

main 方法將來自使用者的輸入作為字串,該字串用作根目錄的路徑。然後使用此字串作為引數呼叫 PrintDirectoryTree 方法。

PrintDirectoryTree(string)

這是處理實際目錄樹列印的兩種方法中的第一種。此方法將表示根目錄路徑的字串作為引數。它檢查路徑是否是實際目錄,如果不是,則丟擲 DirectoryNotFoundException,然後在 catch 塊中處理。如果路徑是真實目錄,則從路徑建立 DirectoryInfo 物件 rootDirectory,並使用 rootDirectory 物件和 RootLevel 呼叫第二個 PrintDirectoryTree 方法,RootLevel 是一個值為零的整數常量。

PrintDirectoryTree(DirectoryInfo, int)

第二種方法處理工作的主要問題。它需要一個 DirectoryInfo 和一個整數作為引數。DirectoryInfo 是當前目錄,整數是目錄相對於根的深度。為了便於閱讀,輸出將縮排當前目錄的每個級別,以便輸出如下所示:

-Root
    -Child 1
    -Child 2
        -Grandchild 2.1
    -Child 3

列印當前目錄後,將檢索其子目錄,然後在每個目錄上呼叫此方法,其深度級別值比當前值大 1。那部分是遞迴:呼叫自身的方法。程式將以這種方式執行,直到它訪問了樹中的每個目錄。當它到達沒有子目錄的目錄時,該方法將自動返回。

此方法還捕獲 UnauthorizedAccessException,如果當前目錄的任何子目錄受系統保護,則丟擲 UnauthorizedAccessException。為了一致性,將在當前縮排級別列印錯誤訊息。

下面的方法提供瞭解決此問題的更基本方法:

internal static void PrintDirectoryTree(string directoryName)
{
    try
    {
        if (!Directory.Exists(directoryName)) return;
        Console.WriteLine(directoryName);
        foreach (var d in Directory.GetDirectories(directoryName))
        {
            PrintDirectoryTree(d);
        }
    }
    catch (Exception e)
    {
        Console.WriteLine(e.Message);
    }
}

這不包括第一種方法的特定錯誤檢查或輸出格式,但它實際上做了同樣的事情。由於它只使用字串而不是 DirectoryInfo,因此無法訪問其他目錄屬性(如許可權)。