【问题标题】:Recursively listing of files and folders in C# [closed]C#中文件和文件夹的递归列表[关闭]
【发布时间】:2013-12-10 09:00:22
【问题描述】:

我编写这段代码是为了在 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)。)

有什么想法或指导吗?

【问题讨论】:

  • 为什么不直接使用 overload of the method you're using 为您处理这个问题?
  • 你能发布你的非递归函数吗?
  • @DanPuzey 更正:那是超载:)
  • @SriramSakthivel:更正!星期一... ;-)
  • 什么是n?如果这是文件数 + 文件夹数,那么是的,O(n) 听起来是正确的(您为每个文件/文件夹做了大量的工作)。

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


【解决方案1】:

使用递归来做这件事的主要问题是:

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

  2. 您使用的内存超出了需要,因为您在堆栈帧中保留了大量数据,而这些数据可能会避免。

至于渐近复杂度,您对每个节点执行一个操作,而不管它的大小,所以它是 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());

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

【讨论】:

  • 非常感谢您的指导。准确的回答。但是,对不起,我仍然不明白你回答的这一部分。 you're performing one operation per node, regardless of it's size, so it's O(n)。你能详细说明一下吗?
  • @nowhewhomustnotbenamed。还有什么好说的。工作量与整棵树中的节点数成正比。具有 2 倍节点数的树将花费 2 倍的时间来遍历。将单个节点添加到大小为 100 的树与大小为 5 的树会增加相同的“成本”。这使它成为线性的。
  • 谢谢。不是很有意义。此外,generic non-recursive 版本也很好用。
  • +1 为好答案 ;)
  • Iirc,对于您没有安全权限的文件夹,内置目录枚举失败。所以例如Directory.EnumerateFiles("C:\\", "*", SearchOption.AllDirectories) 对于启用了 UAC 的用户的非提升程序将失败(它不会让它进入 C:\System 等)。要枚举您有权访问的所有目录,您需要使用自己的代码遍历树,并在每个阶段检查权限。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-21
  • 2013-01-27
  • 1970-01-01
  • 1970-01-01
  • 2016-07-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多