Как фильтровать Directory.EnumerateFiles с несколькими критериями?
У меня есть следующий код:
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? Любые предложения?
Я был бы признателен за любую помощь, являющуюся случайным программистом: -)
Ответы
Ответ 1
Я создал несколько вспомогательных методов для решения этой проблемы, о которой я писал в блоге в начале этого года.
В одной версии используется шаблон регулярного выражения \.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));
}
}
Ответ 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))
Ответ 3
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);
}
Ответ 4
Самый элегантный подход, вероятно:
var directory = new DirectoryInfo(path);
var masks = new[] { "*.mp3", "*.wav" };
var files = masks.SelectMany(directory.EnumerateFiles);
Но это может быть не самое эффективное.
Ответ 5
Как я уже отмечал в комментарии, в то время как вспомогательные методы Микаэля Свенсона - отличные решения, если вы когда-либо пытаетесь что-то сделать для одноразового проекта в спешке, рассмотрите расширение Linq .Union( ). Это позволяет вам объединить две перечислимые последовательности. В вашем случае код будет выглядеть так:
List<string> result = Directory.EnumerateFiles(path,"*.mp3", SearchOption.AllDirectories)
.Union(Directory.EnumerateFiles(path, ".wma", SearchOption.AllDirectories)).ToList();
Это создает и заполняет список результатов в одной строке.
Ответ 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
Для фильтрации с использованием тех же строк списка расширений файлов, что и в открытых диалоговых окнах графического интерфейса, например:
".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));
}