How to filter Directory.EnumerateFiles with multiple criteria?

C#.Net

C# Problem Overview


I have the following code:

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

It works fine and does what I need. Except for one small thing. I would like to find a better way to filter on multiple extensions. I would like to use a string array with filters such as this:

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

What is the most efficient way to do this using NET Framework 4.0/LINQ? Any suggestions?

I'd appreciate any help being an occasional programmer :-)

C# Solutions


Solution 1 - C#

I created some helper methods to solve this which I blogged about earlier this year.

One version takes a regex pattern \.mp3|\.mp4, and the other a string list and runs in parallel.

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

Solution 2 - C#

Stripped from the LINQ context, this comes down to how to find out if a file matches a list of extensions. System.IO.Path.GetExtension() is a better choice here than String.EndsWith(). The multiple || can be replaced with .Contains() or .IndexOf() depending on the collection.

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

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

Solution 3 - C#

The most elegant approach is probably:

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

But it might not be the most efficient.

Solution 4 - C#

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

Solution 5 - C#

As I noted in a comment, while Mikael Svenson's helper methods are great little solutions, if you're ever trying to do something for a one-off project in a hurry again, consider the Linq extension .Union( ). This allows you to join together two enumerable sequences. In your case, the code would look like this:

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

This creates and fills your result list all in one line.

Solution 6 - C#

I solved this problem this way:

string[] formats = {".mp3", ".wma", ".mp4"};

foreach (var file in Directory.EnumerateFiles(folder, "*.*", SearchOption.AllDirectories).Where(x => formats.Any(x.EndsWith)))
{
    // TODO...
}

Solution 7 - C#

I know this is an old post but I came up with a solution people might like to use.

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

Solution 8 - C#

For Filtering using the same File Extensions list strings as GUI Open Dialogs e.g.:

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

Packaged up:

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

Solution 9 - C#

I haven't tested this code in previous versions of .NET.

In .NET6, this can be done as shown below:

public static IEnumerable<string> EnumerateFiles(string path, string[] searchPatterns, SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
    return Directory.EnumerateFiles(path, "*", searchOption)
                    .Where(fileName => searchPatterns.Any(pattern => FileSystemName.MatchesSimpleExpression(pattern, fileName)));
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionYooakimView Question on Stackoverflow
Solution 1 - C#Mikael SvensonView Answer on Stackoverflow
Solution 2 - C#Henk HoltermanView Answer on Stackoverflow
Solution 3 - C#Tom PažourekView Answer on Stackoverflow
Solution 4 - C#Islam YahiateneView Answer on Stackoverflow
Solution 5 - C#KirkaiyaView Answer on Stackoverflow
Solution 6 - C#Витёк СинёвView Answer on Stackoverflow
Solution 7 - C#kmcbreartyView Answer on Stackoverflow
Solution 8 - C#Matthew SheeranView Answer on Stackoverflow
Solution 9 - C#Yevhen CherkesView Answer on Stackoverflow