在C#中递归列出文件和文件夹

时间:2013-11-25 15:08:24

标签: c# data-structures file-io recursion .net-4.0

我编写此代码以递归方式列出c#中的文件和文件夹。

            var filesInTheCurrentDirectory = System.IO.Directory.GetFiles(rootFolder);
            if (!filesInTheCurrentDirectory.Any() && !System.IO.Directory.GetDirectories(rootFolder).ToList().Any())
            {
                return;
            }

            filesInTheCurrentDirectory.ToList().ForEach(file => System.IO.File.AppendAllText("e:\\personal\\tests.txt", System.IO.Path.GetFileName(file) + ":" + rootFolder + "\n"));
            System.IO.Directory.GetDirectories(rootFolder).ToList().ForEach(recursivePrintFolders);

虽然 效果很好 ,但问题是:

  1. 我正在使用递归。这是 best 吗? (我尝试编写一个非递归函数,但由于我们事先不知道每个文件夹的深度),所以它会被推迟。

  2. 如何评估此功能的性能?是OlogN还是O(n)? (我很困惑,因为没有循环版本。据我说,如果有两个for循环,我可以称之为O(n^2)。)

  3. 有任何想法或指导吗?

1 个答案:

答案 0 :(得分:5)

使用递归执行此操作的主要问题是:

  1. 如果树的深度太大,则可能没有足够的堆栈空间。虽然拥有那么深的文件系统结构并不常见,但这并不是不可想象的。

  2. 您使用的内存超出了所需的内存,因为您在堆栈框架中保留了很多可能会避免的数据。

  3. 至于渐近复杂度,你每个节点执行一个操作,无论它的大小如何,所以它是O(n),其中n是所有节点,而不是任何给定深度的节点。

    但是,您可以使用内置方法遍历整个树,从而更有效地处理所有这些问题。它会比你提出的解决方案更有效,即使你的非递归,只需使用:

    foreach(string file in Directory.EnumerateFiles(path, "*", 
        SearchOption.AllDirectories))
    {
        System.IO.File.AppendAllText("e:\\personal\\tests.txt",
            System.IO.Path.GetFileName(file) + ":" + rootFolder + "\n")
    }
    

    虽然该解决方案可能不使用递归,但如果您想知道如何自己编写非递归树遍历,则可以使用这样的通用方法:

    public static IEnumerable<T> Traverse<T>(
        this IEnumerable<T> source
        , Func<T, IEnumerable<T>> childrenSelector)
    {
        var stack = new Stack<T>(source);
        while (stack.Any())
        {
            var next = stack.Pop();
            yield return next;
            foreach (var child in childrenSelector(next))
                stack.Push(child);
        }
    }
    

    在你的情况下,调用它可能看起来像这样:

    var allFiles = new[] { new DirectoryInfo(path) }
        .Traverse(directory => directory.EnumerateDirectories())
        .Select(directory => directory.EnumerateFiles());
    

    请注意,虽然这对于遍历不提供完整遍历的内置方式的树来说很好,但这通常不适合遍历文件系统。您应该使用第一个解决方案,因为它已经针对遍历文件系统的特殊情况进行了高度优化。