75

Is there a way to simplify this linq expression, or is there a better way of doing this?

Directory.GetFiles(dir, "*.*", SearchOption.AllDirectories)
         .Where(s => s.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) ||
                     s.EndsWith(".gif", StringComparison.OrdinalIgnoreCase) ||
                     s.EndsWith(".png", StringComparison.OrdinalIgnoreCase) ||
                     ...);

Basically I want to return all files of a certain extension. Unfortunately, this method isn't very flexible. I'd rather be able to add extensions to a list and have Directory.GetFiles return those extensions. Is that possible?

Andrei
  • 10,918
  • 12
  • 76
  • 110
XSL
  • 2,965
  • 7
  • 38
  • 61
  • 2
    Duplicate: http://stackoverflow.com/questions/163162/can-you-call-directory-getfiles-with-multiple-filters – Jeffrey Knight Nov 09 '12 at 02:08
  • Possible duplicate of [Can you call Directory.GetFiles() with multiple filters?](https://stackoverflow.com/questions/163162/can-you-call-directory-getfiles-with-multiple-filters) – kenny Jun 14 '18 at 18:54

3 Answers3

126

If you would like to do your filtering in LINQ, you can do it like this:

var ext = new List<string> { "jpg", "gif", "png" };
var myFiles = Directory
    .EnumerateFiles(dir, "*.*", SearchOption.AllDirectories)
    .Where(s => ext.Contains(Path.GetExtension(s).TrimStart(".").ToLowerInvariant()));

Now ext contains a list of allowed extensions; you can add or remove items from it as necessary for flexible filtering.

themetis
  • 103
  • 4
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • I can perform tests, but do you know which one would be more efficient? – XSL Nov 09 '12 at 02:13
  • Interesting that it works... From the documentation on `GetFiles`, it doesn't seem like it would accept the `|` like you would expect from a regex. The docs say it only recognizes `*` and `?`, but hey, if it works, it works =) – Tyler Lee Nov 09 '12 at 02:14
  • 2
    Note: This doesn't seem to work on Win7 x64 running .NET 4.5. Throws `Illegal characters in path` due to the pipe. – Simon Whitehead Nov 09 '12 at 02:18
  • @SimonWhitehead Never mind, I misinterpreted [a piece of code in a post on Microsoft's web site](http://social.msdn.microsoft.com/Forums/en/netfxbcl/thread/b0c31115-f6f0-4de5-a62d-d766a855d4d1), where a similarly-named method has been interpreting the pipes internally. I removed the non-working part of the solution (and SSL checked only the first one). – Sergey Kalinichenko Nov 09 '12 at 02:26
  • 2
    Note for the passerby: you might get a performance boost out of the newer .NET 4.0 `Directory.EnumerateFiles` as mentioned in answer by @nekizalb – drzaus Sep 23 '13 at 17:15
31

Doesn't the Directory.GetFiles(String, String) overload already do that? You would just do Directory.GetFiles(dir, "*.jpg", SearchOption.AllDirectories)

If you want to put them in a list, then just replace the "*.jpg" with a variable that iterates over a list and aggregate the results into an overall result set. Much clearer than individually specifying them. =)

Something like...

foreach(String fileExtension in extensionList){
    foreach(String file in Directory.GetFiles(dir, fileExtension, SearchOption.AllDirectories)){
        allFiles.Add(file);
    }
}

(If your directories are large, using EnumerateFiles instead of GetFiles can potentially be more efficient)

Andrei
  • 10,918
  • 12
  • 76
  • 110
Tyler Lee
  • 2,736
  • 13
  • 24
  • Thanks. I like the idea, but I'm not sure how efficient it would be to call GetFiles many times in a loop. – XSL Nov 09 '12 at 02:11
  • See the additional comment about the EnumerateFiles method. – Tyler Lee Nov 09 '12 at 02:12
  • Thanks for the suggestion, but it seems EnumerateFiles is only available for .net 4 but I have to use 3.5. My fault for not mentioning that before, sorry. – XSL Nov 09 '12 at 02:15
  • Ah. Well, there's always the option of running `GetFiles` once before the inner `foreach` and storing the results, then using that stored list in the `foreach`. – Tyler Lee Nov 09 '12 at 02:19
  • 6
    Please be aware of the fact that while using wildcard * there is always chance that you can mess up the intended logic . For instance you have two files Test1.xls and Test2.xlsx and if you use filter *.xls to filter out xls file , Directory.GetFiles ** returns both xls as well as xlsx files **. [Read MSDN for more info]: http://msdn.microsoft.com/en-us/library/ms143316%28v=vs.110%29.aspx – kiran Dec 07 '13 at 05:44
  • effectively, xls and xlsx are the same file that modern day Excel can read? – ina Jan 06 '17 at 19:36
12

I would have done using just single line like

List<string> imageFiles = Directory.GetFiles(dir, "*.*", SearchOption.AllDirectories)
      .Where(file => new string[] { ".jpg", ".gif", ".png" }
      .Contains(Path.GetExtension(file)))
      .ToList();
Klitos Kyriacou
  • 10,634
  • 2
  • 38
  • 70
RkHirpara
  • 175
  • 1
  • 5
  • 7
    This works, of course, but the lambda expression you pass to `.Where()` will create one `string[]` [every time it's invoked](https://stackoverflow.com/questions/39106109/creating-a-constant-but-local-array); that is, for every file under `dir`. If `dir` is, say, `@"C:\"` then this could potentially create tens or hundreds of thousands of short-lived "garbage" arrays. I definitely think it'd be worth it to define your array outside of `.Where()`, even if it means having to write that dreaded second line of code. – Lance U. Matthews Sep 29 '18 at 06:43