使用Enumerable和Lambda过滤文件列表并删除不需要的扩展名

时间:2013-08-21 14:43:20

标签: c# performance lambda ienumerable

我正在使用此代码

       private IEnumerable<String> FindAccessableFiles(string path, string file_pattern, bool recurse)
    {
        IEnumerable<String> emptyList = new string[0];

        if (File.Exists(path))
            return new string[] { path };

        if (!Directory.Exists(path))
            return emptyList;

        var top_directory = new DirectoryInfo(path);

        // Enumerate the files just in the top directory.
        var files = top_directory.EnumerateFiles(file_pattern);
        var filesLength = files.Count();
        var filesList = Enumerable
                  .Range(0, filesLength)
                  .Select(i =>
                  {
                      string filename = null;
                      try
                      {
                          var file = files.ElementAt(i);
                          filename = file.FullName;                              
                      }
                      catch (UnauthorizedAccessException)
                      {
                      }
                      catch (InvalidOperationException)
                      {
                          // ran out of entries
                      }
                      return filename;
                  })
                  .Where(i => null != i);

        if (!recurse)
            return filesList;

        var dirs = top_directory.EnumerateDirectories("*");
        var dirsLength = dirs.Count();
        var dirsList = Enumerable
            .Range(0, dirsLength)
            .SelectMany(i =>
            {
                string dirname = null;
                try
                {
                    var dir = dirs.ElementAt(i);
                    dirname = dir.FullName;
                    return FindAccessableFiles(dirname, file_pattern, recurse);
                }
                catch (UnauthorizedAccessException)
                {
                }
                catch (InvalidOperationException)
                {
                    // ran out of entries
                }

                return emptyList;
            });
        return Enumerable.Concat(filesList, dirsList);
    }

我一直在遇到一些性能问题迭代文件夹中包含100k +文件 - 当我枚举它们时我忽略了所有图像。

我正在尝试解决如何将它们从枚举列表中排除,以便它们从不首先处理,但无法解决如何操作。

我想要排除List<String>扩展名,并使用Contains在代码中执行此操作。

如果我首先从FindAccessableFiles排除它们,我会获得性能提升吗?我该怎么做?如果文件扩展名包含在扩展名列表中,我的初始尝试是抛出异常,但我确信这不是最好的方法。

FindAccessableFiles的目的是生成一个文件列表,以避免GetFiles()抛出异常试图访问导致权限错误的文件的问题。

2 个答案:

答案 0 :(得分:2)

部分问题是FindAccessableFiles正在返回一个IEnumerable<string>实例,每次枚举时都会重新遍历整个目录结构。每次进行枚举时都会重新评估SelectWhere子句,因此您需要多次重复这项昂贵的工作。一个快速解决方法是通过在返回值

上调用.ToList来强制步行一次
return Enumerable.Concat(filesList, dirsList).ToList();

请注意,这将导致此时立即遍历整个枚举。但是它只会完成一次。

如果您仍然看到性能问题,还应考虑其他一些选项

  • 您提到有些图片是您忽略的。我会在击中磁盘之前将它们过滤掉以获取有关它们的信息。在检查路径名称
  • 时,点击磁盘要贵得多
  • 将磁盘移动到后台线程

答案 1 :(得分:1)

我同意JaredPar,你想确保你不重新枚举。您的返回应该有.ToList(),但var files = top_directory.EnumerateFiles(file_pattern);也需要它。

异常处理非常昂贵,因此不建议添加超过您的内容。枚举文件不支持您正在寻找的那种过滤,因此您最终必须在某处手动执行此操作,可能最好这样做:

filename = excludedExtensionList.Any(e => e == file.Extension) ? null : file.FullName;

如果仍然存在性能问题,则必须考虑如何将操作分解为更小的块:如果文件名可靠,您可能会想出一个方案来更改EnumerateFiles pattern(所有文件以“a”开头,然后是“b”,然后是“c”等)。或者如果在同一个文件夹中存在大量垃圾,是否可以更改目录结构,以使不需要的文件始终位于您知道要忽略的子文件夹中?