Cómo filtrar Directory.EnumerateFiles con múltiples criterios?

Tengo el siguiente código:

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

Funciona bien y hace lo que necesito. Excepto por una pequeña cosa. Me gustaría encontrar una mejor manera de filtrar en múltiples extensiones. Me gustaría utilizar una matriz de cadenas con filtros como este:

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

¿Cuál es la forma más eficiente de hacer esto usando NET Framework 4.0 / LINQ? ¿Alguna sugerencia?

Agradecería cualquier ayuda siendo un progtwigdor ocasional 🙂

Creé algunos métodos de ayuda para resolver esto, sobre lo cual publiqué un blog a principios de este año.

Una versión toma un patrón regex \.mp3|\.mp4 , y la otra una lista de cadenas y se ejecuta en paralelo.

 public static class MyDirectory { // Regex version public static IEnumerable 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 GetFiles(string path, string[] searchPatterns, SearchOption searchOption = SearchOption.TopDirectoryOnly) { return searchPatterns.AsParallel() .SelectMany(searchPattern => Directory.EnumerateFiles(path, searchPattern, searchOption)); } } 

Despojado del contexto LINQ, esto se reduce a cómo averiguar si un archivo coincide con una lista de extensiones. System.IO.Path.GetExtension() es una mejor opción aquí que String.EndsWith() . El múltiple || puede reemplazarse con .Contains() o .IndexOf() según la colección.

 var extensions = new HashSet(StringComparer.OrdinalIgnoreCase) { ".mp3", ".wma", ".mp4", ".wav" }; ... s => extensions.Contains(Path.GetExtension(s)) 
 string path = "C:\\"; var result = new List(); 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); } 

Como noté en un comentario, aunque los métodos de ayuda de Mikael Svenson son pequeñas soluciones, si alguna vez intentas hacer algo para un proyecto único de prisa otra vez, considera la extensión de Linq .Union () . Esto le permite unir dos secuencias enumerables. En tu caso, el código sería así:

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

Esto crea y llena su lista de resultados todo en una línea.

El enfoque más elegante es probablemente:

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

Pero podría no ser el más eficiente.

Sé que esta es una publicación anterior, pero se me ocurrió una solución que la gente podría querer usar.

 private IEnumerable 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 directories = foldersFilter.Split(',').SelectMany(pattern => sourceDirectory.EnumerateDirectories(pattern, SearchOption.AllDirectories)); List files = new List(); 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 files2 = fileTypesFilter.Split(',').SelectMany(pattern => sourceDirectory.EnumerateFiles(pattern, SearchOption.AllDirectories)); } 

Para el filtrado utilizando las mismas cadenas de lista de extensiones de archivo como cuadros de diálogo Abrir de GUI, por ejemplo:

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

Empaquetado:

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