tags:

views:

159

answers:

3

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 :-)

+6  A: 

Stripped from the LINQ context, this comes down to ho 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))
Henk Holterman
You need to remove the `*`s if you want to do string comparisons with them.
Gabe
And another suggestion would be to use an overload that allows for case-insensitive checks.
0xA3
Probably better to use a Hashset and a case-insensitive comparison.
Jim Mischel
@Gabe: Of course, copy/paste oversight.
Henk Holterman
@All: Yes, improved several points.
Henk Holterman
You need to include the dot (.mp3). Use string.ToLower() to handle case.
Hans Passant
@Hans, right about the dot but would ToLower() be better than OrdinalIgnoreCase? Just a pick: http://stackoverflow.com/questions/501906/caselessly-comparing-strings-in-c
Henk Holterman
Tricky question, the file system has its own rules. I don't know exactly what they are, I doubt OrdinalIgnoreCase. Luckily it doesn't matter with the given extension strings.
Hans Passant
Found a better question+answer: http://stackoverflow.com/questions/1756724/
Henk Holterman
+1  A: 
        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);
        }
Islam Ibrahim
You also need ".mp3", not "mp3".
Henk Holterman
+1  A: 

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