Как фильтровать каталог.Перечислять файлы с несколькими критериями?


у меня есть следующий код:

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? Есть предложения?

Я был бы признателен за любую помощь в качестве случайного программиста : -)

7 61

7 ответов:

Я создал некоторые вспомогательные методы, чтобы решить эту проблему, которую я блоге ранее в этом году.

одна версия принимает шаблон регулярных выражений \.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));
   }
}

лишенный контекста 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))
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);
}

самый элегантный подход, вероятно:

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

но это может быть не самым эффективным.

Как я отметил в комментарии, в то время как вспомогательные методы Микаэля Свенсона-отличные маленькие решения, если вы когда-либо пытаетесь сделать что-то для одноразового проекта в спешке снова, рассмотрите расширение Linq .Union (). Это позволяет объединить две перечислимые последовательности. В вашем случае код будет выглядеть так:

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

это создает и заполняет список результатов все в одной строке.

Я знаю, что это старый пост, но я придумал решение, которое люди хотели бы использовать.

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));
}

для фильтрации с использованием тех же строк списка расширений файлов, что и 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));
    }