快速估计子文件夹的数量

本文关键字:文件夹 | 更新日期: 2023-09-27 17:59:32

我的C#3.0应用程序应该遍历文件夹并在其中执行一些操作。为了显示有意义的进展,我需要知道文件夹总数。

如果我使用Directory.GetDirectoriesAllDirectories选项,在我的2Tb硬盘上(其中包含大约100K个文件夹),这将花费很长时间,即使是该操作,我也应该显示进度!我能做的唯一有意义的事情就是使用递归Directory.GetDirectories,并向用户提供一些已经找到的目录。然而,这需要比第一种方法更长的时间。

我认为,这两种方法都太慢了。有没有办法更快地得到这个号码?例如,使用PInvoke从一些文件表中提取?还有其他想法吗?

快速估计子文件夹的数量

我的建议是,在获取所有目录时,只向用户显示一个无限滚动的进度条,并且只有在应用程序执行工作时才向用户显示实际进度。

这样,当一切发生时,用户就会知道应用程序正在后台工作。

这类事情很难做到。如果你只是想粗略估计进度条,你不需要太多粒度,对吧?我建议手动遍历目录树,只深入一到两级,以计算出有多少第一级和第二级子目录。然后,每当你点击其中一个分区时,你就可以更新进度条。这应该会给你一个有意义的进度条,而不需要花太多时间来计算。

如果你实现了这一点,你会发现你的第一次预扫描是最慢的,但它会加快下一次(完全)扫描,因为文件夹结构正在被缓存。

可以选择只计数前N(2..4)级中的文件夹。这可能仍然很慢,但这将允许估计的进展。假设所有较低级别都包含相同数量的文件。


第2部分,关于p/Invoke问题

您的主要成本是这里是真正的低级别I/O,(任何)API的开销都可以忽略不计。

EnumerateFiles()(Fx4)替换GetFiles()可能会使您受益。与预扫描相比,主循环更是如此。

探索FindFirstFile和FindNextFile API。我认为它们在您的情况下会工作得更快

我写了一个非常简单的文件枚举。这种进步在数学上是连续的,也就是说,无论发生什么,它都不会在以后变成一个更低的值。这种估计是基于这样一种想法,即所有文件夹都包含相同数量的文件和子文件夹,这显然从来都不是这样,但只要有一个合理的想法就足够了。

几乎没有缓存,尤其是深层结构,所以这应该和直接枚举一样快。

public static IEnumerable<Tuple<string, float>> EnumerateFiles (string root)
{
    var files = Directory.GetFiles (root);
    var dirs = Directory.GetDirectories (root);
    var fact = 1f / (float) (dirs.Length + 1); // this makes for a rough estimate
    for (int i = 0; i < files.Length; i++) {
        var file = files[i];
        var f = (float) i / (float) files.Length;
        f *= fact;
        yield return new Tuple<string, float> (file, f);
    }
    for (int i = 0; i < dirs.Length; i++) {
        var dir = dirs[i];
        foreach (var tuple in EnumerateFiles (dir)) {
            var f = tuple.Item2;
            f *= fact;
            f += (i + 1) * fact;
            yield return new Tuple<string, float> (tuple.Item1, f);
        }
    }
}