【问题标题】:How to filter Directory.EnumerateFiles with multiple criteria?如何过滤具有多个条件的 Directory.EnumerateFiles?
【发布时间】:2010-09-20 18:02:57
【问题描述】:

我有以下代码:

List<string> result = new List<string>();

foreach (string file in Directory.EnumerateFiles(path,"*.*",  
      SearchOption.AllDirectories)
      .Where(s => s.EndsWith(".mp3") || s.EndsWith(".wma")))
       {
          result.Add(file);                 
       }

它工作正常,可以满足我的需要。除了一件小事。我想找到一种更好的方法来过滤多个扩展。我想使用带有过滤器的字符串数组,例如:

string[] extensions = { "*.mp3", "*.wma", "*.mp4", "*.wav" };

使用 NET Framework 4.0/LINQ 最有效的方法是什么?有什么建议吗?

我会很感激作为临时程序员的任何帮助:-)

【问题讨论】:

  • 您应该考虑并行运行每个扩展搜索。我在回答中创建了一些有用的辅助方法。一个接受正则表达式,一个接受字符串列表。
  • 这是一个非常老问题(@MikaelSvenson 已经适当回答了),但另一种选择是使用 Enumerable 扩展名 .Union(),如下所示:foreach (var Directory.EnumerateFiles(path, ".mp3", SearchOption.AllDirectories).Union(Directory.EnumerateFiles(path, ".wma", SearchOption.AllDirectories)) { ... }跨度>

标签: c# .net


【解决方案1】:

我创建了一些辅助方法来解决这个问题,我在今年早些时候 blogged

一个版本采用正则表达式模式\.mp3|\.mp4,另一个采用字符串列表并并行运行。

public static class MyDirectory
{   // Regex version
   public static IEnumerable<string> GetFiles(string path, 
                       string searchPatternExpression = "",
                       SearchOption searchOption = SearchOption.TopDirectoryOnly)
   {
      Regex reSearchPattern = new Regex(searchPatternExpression, RegexOptions.IgnoreCase);
      return Directory.EnumerateFiles(path, "*", searchOption)
                      .Where(file =>
                               reSearchPattern.IsMatch(Path.GetExtension(file)));
   }

   // Takes same patterns, and executes in parallel
   public static IEnumerable<string> GetFiles(string path, 
                       string[] searchPatterns, 
                       SearchOption searchOption = SearchOption.TopDirectoryOnly)
   {
      return searchPatterns.AsParallel()
             .SelectMany(searchPattern => 
                    Directory.EnumerateFiles(path, searchPattern, searchOption));
   }
}

【讨论】:

  • 感谢您的良好实施。什么是最终在 WPF 屏幕上显示结果的好(有效)方法?我打算用你的并行方法来获取文件。如果我使用 foreach 迭代结果并将它们存储在列表中,然后将它们加载到屏幕上怎么办?
  • 您可以绑定到任一方法的输出,因为绑定将为您枚举所有结果。无需先将其存储在单独的列表中。最有效的方法是在枚举项目时开始显示项目。我不是 WPF 专家,但我想你应该能够通过一些信号来渲染每个项目。
  • 很好的例子!只是要注意这两种方法中的每一种的一些特征......使用PARALLEL 方法,搜索不区分大小写,并且您将获得的结果将是乱序的。使用REGEX 方法,搜索是区分大小写的(除非您使用"(?i)\.mp3$|\.mp4$" 之类的东西),并且您将获得的结果将按照您的预期进行。我进行了测试,发现并行版本的运行速度可能会稍微快一点,但所有这些差异都非常小。
  • @ArvoBowen 很好地理解了区分大小写的比较,并在代码中添加了一个正则表达式
  • 这是一个很好的解决方案;谢谢!仅供参考:我遇到了追溯到 IEnumerable 的性能问题(主要集中在我在几个地方使用的 Count( ) 方法,但这并不是唯一的性能问题)。我的列表有大约 4700 个文件名。我最终在列表上做了一个 .ToArray( ) 并将所有内容都作为一个数组处理;您需要一次性支付将列表转换为数组的费用,但此后明显更快的性能大大缓解了这种情况。
【解决方案2】:

从 LINQ 上下文中剥离,这归结为如何找出文件是否与扩展名列表匹配。 System.IO.Path.GetExtension() 是比String.EndsWith() 更好的选择。多个|| 可以替换为.Contains().IndexOf(),具体取决于集合。

var extensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)  
   {  ".mp3", ".wma", ".mp4", ".wav" };

...  s => extensions.Contains(Path.GetExtension(s))

【讨论】:

  • 如果你想和*s进行字符串比较,你需要删除它们。
  • 另一个建议是使用允许不区分大小写检查的重载。
  • 使用 Hashset 和不区分大小写的比较可能会更好。
  • 您需要包含点 (.mp3)。使用 string.ToLower() 处理大小写。
  • @Hans,关于点,但是 ToLower() 会比 OrdinalIgnoreCase 更好吗?随便选一个:stackoverflow.com/questions/501906/…
【解决方案3】:

最优雅的方法可能是:

var directory = new DirectoryInfo(path);
var masks = new[] { "*.mp3", "*.wav" };
var files = masks.SelectMany(directory.EnumerateFiles);

但这可能不是最有效的。

【讨论】:

  • 如何包含子文件夹?
  • @AsenKasimov EnumerateFiles 方法有一个带有参数的重载,您可以指定该参数在子文件夹中也进行搜索。
  • 不错,但需要简短编辑:masks.SelectMany(m =&gt; dinf.EnumerateFiles(m));
【解决方案4】:
string path = "C:\\";
var result = new List<string>();
string[] extensions = { ".mp3", ".wma", ".mp4", ".wav" };

foreach (string file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories)
    .Where(s => extensions.Any(ext => ext == Path.GetExtension(s))))
{
    result.Add(file);
    Console.WriteLine(file);
}

【讨论】:

  • 你还需要“.mp3”,而不是“mp3”。
  • 感谢完美... 在我的情况下我需要在 .Where ... 之前添加 .ToArray() 没有这个 LINQ 查询不起作用。
【解决方案5】:

正如我在评论中指出的那样,虽然 Mikael Svenson 的辅助方法是很棒的小解决方案,但如果您再次急于尝试为一次性项目做某事,请考虑 Linq 扩展 .Union( )。这允许您将两个可枚举序列连接在一起。在您的情况下,代码如下所示:

List<string> result = Directory.EnumerateFiles(path,"*.mp3", SearchOption.AllDirectories)
.Union(Directory.EnumerateFiles(path, ".wma", SearchOption.AllDirectories)).ToList();

这会在一行中创建并填充您的结果列表。

【讨论】:

  • 优雅,避免C#枚举所有文件,允许文件系统尽其所能优化。
【解决方案6】:

我知道这是一篇旧帖子,但我想出了一个人们可能喜欢使用的解决方案。

private IEnumerable<FileInfo> FindFiles()
{
    DirectoryInfo sourceDirectory = new DirectoryInfo(@"C:\temp\mydirectory");
    string foldersFilter = "*bin*,*obj*";
    string fileTypesFilter = "*.mp3,*.wma,*.mp4,*.wav";

    // filter by folder name and extension
    IEnumerable<DirectoryInfo> directories = foldersFilter.Split(',').SelectMany(pattern => sourceDirectory.EnumerateDirectories(pattern, SearchOption.AllDirectories));
    List<FileInfo> files = new List<FileInfo>();
    files.AddRange(directories.SelectMany(dir => fileTypesFilter.Split(',').SelectMany(pattern => dir.EnumerateFiles(pattern, SearchOption.AllDirectories))));

    // Pick up root files
    files.AddRange(fileTypesFilter.Split(',').SelectMany(pattern => sourceDirectory.EnumerateFiles(fileTypesFilter, SearchOption.TopDirectoryOnly)));

    // filter just by extension
    IEnumerable<FileInfo> files2 = fileTypesFilter.Split(',').SelectMany(pattern => sourceDirectory.EnumerateFiles(pattern, SearchOption.AllDirectories));
}

【讨论】:

    【解决方案7】:

    我这样解决了这个问题:

    string[] formats = {".mp3", ".wma", ".mp4"};
    
    foreach (var file in Directory.EnumerateFiles(folder, "*.*", SearchOption.AllDirectories).Where(x => formats.Any(x.EndsWith)))
    {
        // TODO...
    }
    

    【讨论】:

      【解决方案8】:

      使用与 GUI 打开对话框相同的文件扩展名列表字符串进行过滤,例如:

      ".exe,.pdb".Split(',', ';', '|').SelectMany(_ => Directory.EnumerateFiles(".", "*" + _, searchOptions)
      

      打包:

          public static IEnumerable<string> EnumerateFilesFilter(string path, string filesFilter, SearchOption searchOption = SearchOption.TopDirectoryOnly)
          {
              return filesFilter.Split(',', ';', '|').SelectMany(_ => Directory.EnumerateFiles(path, "*" + _, searchOption));
          }
      

      【讨论】:

        猜你喜欢
        • 2020-08-20
        • 1970-01-01
        • 2016-07-08
        • 2015-07-14
        • 1970-01-01
        • 2013-01-18
        • 2019-08-25
        • 1970-01-01
        • 2021-07-05
        相关资源
        最近更新 更多