2010-09-20 237 views
57

我有以下代碼:如何篩選具有多個條件的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框架4.0/LINQ?有什麼建議麼?

我想感謝所有幫助是一個偶然的程序員:-)

+0

你應該考慮並行運行每個擴展搜索。我在答案中創建了一些有用的幫助器方法。一個採用正則表達式,另一個採用字符串列表。 – 2010-09-20 18:51:28

+0

這是一個非常*舊的問題(已經適當地回答@MikaelSvenson),但另一種選擇是使用Enumerable擴展.Union(),如下所示:foreach(Directory.EnumerateFiles(path,「* .mp3 「,SearchOption.AllDirectories).Union(Directory.EnumerateFiles(path,」* .wma「,SearchOption.AllDirectories)){...} – Kirkaiya 2015-03-24 20:44:48

回答

65

我創造了一些輔助方法這是我今年blogged約早期解決。

一個版本採用正則表達式\.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)); 
    } 
} 
+0

感謝您的良好實施。什麼可以是一個好的(有效)的方式來最終在WPF屏幕上顯示結果?我打算使用你的並行方法來獲取文件。如果我使用foreach來迭代結果並將它們存儲在List中,並將它們加載到屏幕上呢? – 2012-11-04 08:06:56

+0

您可以將其綁定到任一方法的輸出,因爲綁定會枚舉所有結果。無需首先將其存儲在單獨的列表中。最有效的方法是在列舉項目時開始顯示項目。我不是WPF專家,但我想你應該可以通過某些信號渲染每個項目。 – 2012-11-04 12:56:00

+0

好例子!只需注意兩種方法中的每一種的一些特徵...使用'PARALLEL'方法,搜索不區分大小寫,並且您將得到的結果將會失序。使用'REGEX'方法,搜索區分大小寫(除非您使用''(?i)\。mp3 $ | \ .mp4 $「')之類的東西,並且您將得到的結果將按照您預期的順序排列。我已經運行了測試,發現並行版本可能會更快地運行SLIGHT,但總的來說它是一個非常小的差異。 – 2015-10-19 15:56:31

24

從LINQ背景下剝離,這涉及到如何找出一個文件相匹配擴展名的列表。這裏比String.EndsWith()更好選擇System.IO.Path.GetExtension()。取決於集合,可以用.Contains().IndexOf()代替多個||

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

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

如果你想對它們進行字符串比較,你需要刪除'*'s 。 – Gabe 2010-09-20 18:17:32

+0

另一個建議是使用允許進行不區分大小寫檢查的重載。 – 2010-09-20 18:20:50

+1

可能更適合使用Hashset和不區分大小寫的比較。 – 2010-09-20 18:22:02

16
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); 
} 
+0

你還需要「.mp3」,而不是「mp3」。 – 2010-09-20 18:38:55

+0

感謝作品完美... **在我的情況**我需要添加.ToArray()before.Where ...沒有這個LINQ查詢不起作用。 – Equiman 2016-03-22 13:51:48

3

我知道這是一箇舊帖子,但我想出了一個人們可能會喜歡使用的解決方案。

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

正如我在評論指出,雖然的Mikael史雲遜的助手方法是偉大的小的解決方案,如果你曾經嘗試再次做點事着急一次性項目,考慮的LINQ擴展。 Union()。這允許您將兩個枚舉序列連接在一起。在你的情況下,代碼將如下所示:

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

這會在一行中創建並填充結果列表。

+1

優雅,避免使用C#枚舉所有文件,允許文件系統儘可能優化。 – 2017-11-13 16:25:46

8

最優雅的方法可能是:

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

但它未必是最有效的。

0

用於過濾使用相同的文件擴展名列表的字符串作爲GUI打開的對話框例如: -

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