我正在尝试使用Directory.GetFiles()方法来检索多种类型的文件列表,例如mp3jpg。我尝试了以下两种方法,但都没有碰到运气:

Directory.GetFiles("C:\path", "*.mp3|*.jpg", SearchOption.AllDirectories);
Directory.GetFiles("C:\path", "*.mp3;*.jpg", SearchOption.AllDirectories);


有没有办法在一个电话中做到这一点?

评论

附带说明一下,使用GetFiles搜索模式来过滤扩展名是不安全的,例如,您有两个文件Test1.xls和Test2.xlsx,并且您想要使用搜索模式* .xls过滤出xls文件,但是GetFiles都返回了Test1 .xls和Test2.xlsx。阅读注释部分以获取更多信息

那么如何预防呢?

@kiran那不安全吗?看起来像功能而不是错误。

那么如何预防呢?使用?.xls仅能正确过滤xls文件,例如,不会包括xlsx文件。

#1 楼

对于.NET 4.0和更高版本,

var files = Directory.EnumerateFiles("C:\path", "*.*", SearchOption.AllDirectories)
            .Where(s => s.EndsWith(".mp3") || s.EndsWith(".jpg"));


对于早期版本的.NET,

var files = Directory.GetFiles("C:\path", "*.*", SearchOption.AllDirectories)
            .Where(s => s.EndsWith(".mp3") || s.EndsWith(".jpg"));


编辑:请阅读评论。 Paul Farry建议的改进以及Christian.K指出的内存/性能问题都很重要。

评论


伙计,我不得不更多地考虑LINQ。不错的解决方案!

– Ken Pespisa
09年9月23日在2:29

只需确保您了解其中的含义即可:这将返回字符串数组中的所有文件,然后根据您指定的扩展名对其进行过滤。如果“ C:\ Path”下面没有很多文件,那么这可能不是一个大问题,但可能是“ C:\”或类似内容上的内存/性能问题。

– Christian.K
2010-2-14在12:13

... 2年后:不错的代码,但是要当心,如果您有一个以.JPG结尾的文件,它将无法实现。最好添加s.ToLower()。Endswith ...

–Stormenet
2010年5月5日,9:35

您可以只使用s.EndsWith(“。mp3”,StringComparison.OrdinalIgnoreCase)

– Paul Farry
2010年5月31日在22:58

请注意,在.NET 4.0中,可以将Directory.GetFiles替换为Directory.EnumerateFiles,msdn.microsoft.com / zh-cn / library / dd383571.aspx,这将避免@ Christian.K提到的内存问题。

–吉姆·米歇尔(Jim Mischel)
2011-12-22 22:58

#2 楼

怎么样:

private static string[] GetFiles(string sourceFolder, string filters, System.IO.SearchOption searchOption)
{
   return filters.Split('|').SelectMany(filter => System.IO.Directory.GetFiles(sourceFolder, filter, searchOption)).ToArray();
}


我在这里(在评论中)找到了它:http://msdn.microsoft.com/zh-cn/library/wz42302f.aspx

评论


我猜这避免了最受好评的答案的潜在记忆陷阱?在这种情况下,应该给它更高的评价!

– Dan W
13年2月1日在18:48

@DanW评分最高的答案肯定会增加内存负担,但我认为这不应该是这样的问题。我也喜欢这个答案,但是实际上比接受的答案要慢很多。检查此SpeedTest

– OttO
13年2月13日在22:37

谢谢。很高兴看到它的运行速度只有它的两倍-在此期间,我会坚持下去。

– Dan W
13年2月17日在19:47

如果只有两个扩展名,它的速度只有原来的两倍。如果您有X个扩展名的列表,那么它将慢X倍。因为在这里您多次调用函数Directory.GetFiles,而在其他解决方案中,它仅被调用一次。

–奥斯卡·埃莫西利亚(Oscar Hermosilla)
16年5月12日在13:25

@OscarHermosilla One可以使用Parallel.ForEach并行获取它们

–FindOutIslamNow
18年6月21日在12:22

#3 楼

如果您有大量要检查的扩展,可以使用以下方法。我不想创建很多OR语句,所以我修改了lette的内容。

string supportedExtensions = "*.jpg,*.gif,*.png,*.bmp,*.jpe,*.jpeg,*.wmf,*.emf,*.xbm,*.ico,*.eps,*.tif,*.tiff,*.g01,*.g02,*.g03,*.g04,*.g05,*.g06,*.g07,*.g08";
foreach (string imageFile in Directory.GetFiles(_tempDirectory, "*.*", SearchOption.AllDirectories).Where(s => supportedExtensions.Contains(Path.GetExtension(s).ToLower())))
{
    //do work here
}


评论


请帮我解决这个问题...当我打印imageFile时,它给出了它的总路径。如何将其缩小为文件名。

–那雷什
11年5月24日在8:03

System.IO.Path.GetFileName(imageFile)

– jnoreiga
2011年5月25日17:43

Path.GetExtension返回“ .ext”,而不是“ * .ext”(至少在3.5+中)。

–可为空
2012年1月4日20:43

仅供参考:您需要使用System.Linq作为.where(

– jnoreiga
2012年3月28日在21:20

存在潜在的缺陷。我们已经过去了很长时间,要求扩展名必须恰好是三个字符。假设您可能遇到一个带有.abc的文件,并且supportedExtensions包含.abcd。将匹配,尽管不匹配。要修复:supportedExtensions =“ .jpg | .abcd |”;与.Contains(Path.GetExtension(s).ToLower()+“ |”)。也就是说,在测试中包括分隔符。重要说明:您的分隔符也必须在supportedExceptions中的LAST条目之后。

–ToolmakerSteve
18-4-2在13:10



#4 楼

for

var exts = new[] { "mp3", "jpg" };


您可以:

public IEnumerable<string> FilterFiles(string path, params string[] exts) {
    return
        Directory
        .EnumerateFiles(path, "*.*")
        .Where(file => exts.Any(x => file.EndsWith(x, StringComparison.OrdinalIgnoreCase)));
}




不要忘记新的.NET4 Directory.EnumerateFiles可以提高性能(Directory.EnumerateFiles与Directory.GetFiles之间有什么区别?)
“ IgnoreCase”应该比“ ToLower”更快(.EndsWith("aspx", StringComparison.OrdinalIgnoreCase)而不是.ToLower().EndsWith("aspx")

当您拆分过滤器并合并结果时,EnumerateFiles的真正好处就会显现出来:

public IEnumerable<string> FilterFiles(string path, params string[] exts) {
    return 
        exts.Select(x => "*." + x) // turn into globs
        .SelectMany(x => 
            Directory.EnumerateFiles(path, x)
            );
}


如果您不必将它们变成球形,它会更快一点(即exts = new[] {"*.mp3", "*.jpg"}已经)。

基于以下LinqPad测试的性能评估(注意:Perf仅仅重复了10000次委托)
https://gist.github.com/zaus/7454021

(由于这个问题明确要求不使用LINQ,因此从“重复”中转发和扩展):System.IO.Directory.GetFiles的多个文件扩展名searchPattern)

评论


您的意思是“如果您不必将它们变成问题,我的速度就会更快”?是O(1)还是O(n)(关于文件数,而不是扩展名数)?我会猜想它是O(1)(或关于扩展数的O(n)),可能在几个cpu周期的范围内……如果是这种情况,那么-性能方面-可以忽略不计

–BatteryBackupUnit
2014年7月22日12:39



@BatteryBackupUnit是的,具有2个扩展名的10k次代表,glob与str的差是3ms,所以从技术上讲可以忽略不计(请参阅perf结果链接),但不知道需要过滤多少个扩展名,我认为值得指出的是区别;我要由您自己决定“简化用法”(即.FilterFiles(path,“ jpg”,“ gif”))是否比“显式glob”(即.FilterFiles(path,“ * .jpg”,“ * .gif“))。

–drzaus
2014年7月23日15:42

很好,谢谢。抱歉,我以某种方式跳过了该github链接。也许我应该调整屏幕颜色设置:)

–BatteryBackupUnit
2014年7月23日在18:46

这是否支持大写扩展名,例如.JPG或.MKV?

–华宇
16年4月23日在7:18

SelectMany解决方案的缺陷在于,每个文件扩展名传入后,它将遍历所有文件。

– 26之17
16-09-15在13:12

#5 楼

我知道这是个老问题,但是LINQ:(.NET40 +)

var files = Directory.GetFiles("path_to_files").Where(file => Regex.IsMatch(file, @"^.+\.(wav|mp3|txt)$"));


评论


好主意。考虑使用file.ToLower()轻松匹配大写扩展名。并且为什么不首先提取扩展名,所以Regex不必检查整个路径:Regex.IsMatch(Path.GetExtension(file).ToLower(),@“ \。(wav | mp3 | txt)”);

–ToolmakerSteve
18年4月2日在13:17

#6 楼

还有一个下降的解决方案,似乎没有任何内存或性能开销,而且非常优雅:

string[] filters = new[]{"*.jpg", "*.png", "*.gif"};
string[] filePaths = filters.SelectMany(f => Directory.GetFiles(basePath, f)).ToArray();


评论


我想我可以对其进行编辑,以便使用新的字符串变量和Split函数接受无限数量的扩展。但是即使如此,这比jnoreiga的解决方案还更好吗?它更快吗?更少的内存消耗?

–括号
17年7月12日在17:33



需要权衡。此方法多次调用GetFiles,每个过滤器一次。在某些情况下,这些多次调用可能会带来巨大的“性能开销”。它具有重要的优点,即每个GetFiles仅返回具有匹配文件路径的数组。我希望这通常是一个很好的性能结果,甚至可能是更高的性能,但这需要进行测试。如果GetFiles明显比EnumerateFiles快,那么这可能是最好的方法。还要注意,当直接使用IEnumerable时,可以省略最后的“ .ToArray()”。

–ToolmakerSteve
18年4月2日在13:32



#7 楼

使用Linq的另一种方法,但不必返回所有内容并在内存中进行过滤。

var files = Directory.GetFiles("C:\path", "*.mp3", SearchOption.AllDirectories).Union(Directory.GetFiles("C:\path", "*.jpg", SearchOption.AllDirectories));


实际上是对GetFiles()的2次调用,但我认为这与问题的精神,并将其一一列举。

评论


那么为什么要使用Linq?它会比使用List和addrange更快吗?

– ThunderGr
13年11月1日13:16

我不知道什么会更快,也不认为这是一个重要的问题。在几乎任何将您的代码用于解决此问题的位置的任何地方,性能上的差异都可以忽略不计。问题应该是什么更易读,以减轻将来代码的可维护性。我认为这是一个合理的答案,因为它放在了一个源代码行中,我认为这是问题所希望的一部分,必要的调用并清楚地表达了该行的意图。 list和addrange分散了多个步骤来完成同一件事。

–戴夫·雷尔(Dave Rael)
2013年11月1日17:13

#8 楼

不。请尝试以下操作:

List<string> _searchPatternList = new List<string>();
    ...
    List<string> fileList = new List<string>();
    foreach ( string ext in _searchPatternList )
    {
        foreach ( string subFile in Directory.GetFiles( folderName, ext  )
        {
            fileList.Add( subFile );
        }
    }

    // Sort alpabetically
    fileList.Sort();

    // Add files to the file browser control    
    foreach ( string fileName in fileList )
    {
        ...;
    }


来自:http://blogs.msdn.com/markda/archive/2006/04/20/580075.aspx

#9 楼

Let

var set = new HashSet<string> { ".mp3", ".jpg" };


然后

Directory.GetFiles(path, "*.*", SearchOption.AllDirectories)
         .Where(f => set.Contains(
             new FileInfo(f).Extension,
             StringComparer.OrdinalIgnoreCase));




from file in Directory.GetFiles(path, "*.*", SearchOption.AllDirectories)
from ext in set
where String.Equals(ext, new FileInfo(file).Extension, StringComparison.OrdinalIgnoreCase)
select file;


评论


getfiles没有发布过载。

– nawfal
2012年7月27日13:51

#10 楼

我无法使用.Where方法,因为我正在.NET Framework 2.0中编程(仅.NET Framework 3.5+支持Linq)。

以下代码不区分大小写(因此也会列出.CaB.cab)。

string[] ext = new string[2] { "*.CAB", "*.MSU" };

foreach (string found in ext)
{
    string[] extracted = Directory.GetFiles("C:\test", found, System.IO.SearchOption.AllDirectories);

    foreach (string file in extracted)
    {
        Console.WriteLine(file);
    }
}


#11 楼

以下函数搜索多个模式,并用逗号分隔。您也可以指定排除,例如:“!web.config”将搜索所有文件,而排除“ web.config”。模式可以混合。

private string[] FindFiles(string directory, string filters, SearchOption searchOption)
{
    if (!Directory.Exists(directory)) return new string[] { };

    var include = (from filter in filters.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) where !string.IsNullOrEmpty(filter.Trim()) select filter.Trim());
    var exclude = (from filter in include where filter.Contains(@"!") select filter);

    include = include.Except(exclude);

    if (include.Count() == 0) include = new string[] { "*" };

    var rxfilters = from filter in exclude select string.Format("^{0}$", filter.Replace("!", "").Replace(".", @"\.").Replace("*", ".*").Replace("?", "."));
    Regex regex = new Regex(string.Join("|", rxfilters.ToArray()));

    List<Thread> workers = new List<Thread>();
    List<string> files = new List<string>();

    foreach (string filter in include)
    {
        Thread worker = new Thread(
            new ThreadStart(
                delegate
                {
                    string[] allfiles = Directory.GetFiles(directory, filter, searchOption);
                    if (exclude.Count() > 0)
                    {
                        lock (files)
                            files.AddRange(allfiles.Where(p => !regex.Match(p).Success));
                    }
                    else
                    {
                        lock (files)
                            files.AddRange(allfiles);
                    }
                }
            ));

        workers.Add(worker);

        worker.Start();
    }

    foreach (Thread worker in workers)
    {
        worker.Join();
    }

    return files.ToArray();

}


用法:

foreach (string file in FindFiles(@"D:8.2.11", @"!*.config, !*.js", SearchOption.AllDirectories))
            {
                Console.WriteLine(file);
            }


#12 楼

List<string> FileList = new List<string>();
DirectoryInfo di = new DirectoryInfo("C:\DirName");

IEnumerable<FileInfo> fileList = di.GetFiles("*.*");

//Create the query
IEnumerable<FileInfo> fileQuery = from file in fileList
                                  where (file.Extension.ToLower() == ".jpg" || file.Extension.ToLower() == ".png")
                                  orderby file.LastWriteTime
                                  select file;

foreach (System.IO.FileInfo fi in fileQuery)
{
    fi.Attributes = FileAttributes.Normal;
    FileList.Add(fi.FullName);
}


评论


file.Extension.ToLower()是错误的做法。

– abatishchev
2012年7月27日14:35

那我们应该用什么呢? @abatishchev

– Nitin S
13年6月18日在10:56

@Nitin:String.Equals(a,b,StringComparison.OrdinalIgnoreCase)

– abatishchev
13年6月18日在17:34

实际上,我更喜欢file.Extension.Equals(“。jpg”,StringComparison.OrdinalIgnoreCase)。它似乎比.ToLower或.ToUpper更快,所以他们在我搜索的所有地方都说。实际上,.Equals也比==快,因为==会调用.Equals并检查null(因为您不能执行null.Equals(null))。

– ThunderGr
13年11月1日13:19



#13 楼

在.NET 2.0中(无Linq):

public static List<string> GetFilez(string path, System.IO.SearchOption opt,  params string[] patterns)
{
    List<string> filez = new List<string>();
    foreach (string pattern in patterns)
    {
        filez.AddRange(
            System.IO.Directory.GetFiles(path, pattern, opt)
        );
    }


    // filez.Sort(); // Optional
    return filez; // Optional: .ToArray()
}


然后使用它:

foreach (string fn in GetFilez(path
                             , System.IO.SearchOption.AllDirectories
                             , "*.xml", "*.xml.rels", "*.rels"))
{}


#14 楼

DirectoryInfo directory = new DirectoryInfo(Server.MapPath("~/Contents/"));

//Using Union

FileInfo[] files = directory.GetFiles("*.xlsx")
                            .Union(directory
                            .GetFiles("*.csv"))
                            .ToArray();


评论


如何浏览文件以获取路径?谢谢

– Si8
12月16日20:55

#15 楼

刚刚找到了另一种方法。仍然不是一项操作,而是将其丢弃以查看其他人对此有何看法。

private void getFiles(string path)
{
    foreach (string s in Array.FindAll(Directory.GetFiles(path, "*", SearchOption.AllDirectories), predicate_FileMatch))
    {
        Debug.Print(s);
    }
}

private bool predicate_FileMatch(string fileName)
{
    if (fileName.EndsWith(".mp3"))
        return true;
    if (fileName.EndsWith(".jpg"))
        return true;
    return false;
}


#16 楼

那么

string[] filesPNG = Directory.GetFiles(path, "*.png");
string[] filesJPG = Directory.GetFiles(path, "*.jpg");
string[] filesJPEG = Directory.GetFiles(path, "*.jpeg");

int totalArraySizeAll = filesPNG.Length + filesJPG.Length + filesJPEG.Length;
List<string> filesAll = new List<string>(totalArraySizeAll);
filesAll.AddRange(filesPNG);
filesAll.AddRange(filesJPG);
filesAll.AddRange(filesJPEG);


#17 楼

如果您使用的是VB.NET(或将依赖项导入到C#项目中),则实际上存在一种方便的方法,该方法可以过滤多个扩展名:

Microsoft.VisualBasic.FileIO.FileSystem.GetFiles("C:\path", Microsoft.VisualBasic.FileIO.SearchOption.SearchAllSubDirectories, new string[] {"*.mp3", "*.jpg"});


在VB.NET可以通过My-namespace进行访问:

My.Computer.FileSystem.GetFiles("C:\path", FileIO.SearchOption.SearchAllSubDirectories, {"*.mp3", "*.jpg"})


不幸的是,这些便捷方法不像Directory.EnumerateFiles()那样支持延迟评估的变体。 >

评论


这很容易是最好的答案,但更棘手的是公认的答案。爱得如此。

–罗比·科恩(Robbie Coyne)
19年11月18日在4:09

#18 楼

将所需的扩展名设为一个字符串,即“ .mp3.jpg.wma.wmf”,然后检查每个文件是否包含所需的扩展名。
由于不使用LINQ,因此它与.net 2.0兼容。

string myExtensions=".jpg.mp3";

string[] files=System.IO.Directory.GetFiles("C:\myfolder");

foreach(string file in files)
{
   if(myExtensions.ToLower().contains(System.IO.Path.GetExtension(s).ToLower()))
   {
      //this file has passed, do something with this file

   }
}


这种方法的优点是您可以添加或删除扩展名而无需编辑代码,即添加png图片,只需编写myExtensions =“。jpg.mp3.png”。 br />

评论


它不知道是什么

–括号
17年7月12日在17:22

#19 楼

/// <summary>
/// Returns the names of files in a specified directories that match the specified patterns using LINQ
/// </summary>
/// <param name="srcDirs">The directories to seach</param>
/// <param name="searchPatterns">the list of search patterns</param>
/// <param name="searchOption"></param>
/// <returns>The list of files that match the specified pattern</returns>
public static string[] GetFilesUsingLINQ(string[] srcDirs,
     string[] searchPatterns,
     SearchOption searchOption = SearchOption.AllDirectories)
{
    var r = from dir in srcDirs
            from searchPattern in searchPatterns
            from f in Directory.GetFiles(dir, searchPattern, searchOption)
            select f;

    return r.ToArray();
}


#20 楼

不,...我相信您必须拨打所需数量的电话。

我自己创建一个函数,在具有所需扩展名的字符串上获取数组,然后在该数组上进行迭代,进行所有必要的调用。该函数将返回与我发送的扩展名匹配的文件的一般列表。

希望有帮助。

#21 楼

我想知道为什么发布了这么多的“解决方案”吗?

如果我的新手对GetFiles的工作方式的理解是正确的,那么只有两种选择,并且上述任何一种解决方案都可以归结为:


GetFiles,然后进行过滤:速度很快,但是由于存储开销直到应用了过滤器才造成内存杀手
GetFiles时过滤:设置的过滤器越多,速度越慢,但内存不足用法,因为没有存储开销。这在上面的一篇文章中以令人印象深刻的基准进行了说明:每个过滤器选项都会导致单独的GetFile操作,因此硬盘的同一部分将被读取多次。

在我看来,选项1)更好,但在C:\等文件夹上使用SearchOption.AllDirectories会占用大量内存。因此,我只是使用选项1制定了遍历所有子文件夹的递归子方法。 )

这应该只导致每个文件夹上有1个GetFiles操作,因此操作较快(选项1),但仅需使用少量内容在每个子文件夹的读取之后都应用了过滤器,因此会占用大量内存->在每个子文件夹之后都将删除开销。

如果我写错了,请纠正我。正如我所说的那样,我对编程很陌生,但是想对事物有更深入的了解,最终可以擅长于此:)

#22 楼

我遇到了同样的问题,却找不到正确的解决方案,因此我编写了一个名为GetFiles的函数:

/// <summary>
/// Get all files with a specific extension
/// </summary>
/// <param name="extensionsToCompare">string list of all the extensions</param>
/// <param name="Location">string of the location</param>
/// <returns>array of all the files with the specific extensions</returns>
public string[] GetFiles(List<string> extensionsToCompare, string Location)
{
    List<string> files = new List<string>();
    foreach (string file in Directory.GetFiles(Location))
    {
        if (extensionsToCompare.Contains(file.Substring(file.IndexOf('.')+1).ToLower())) files.Add(file);
    }
    files.Sort();
    return files.ToArray();
}


此函数仅调用一次Directory.Getfiles()

例如调用这样的函数:

string[] images = GetFiles(new List<string>{"jpg", "png", "gif"}, "imageFolder");


编辑:要获取一个具有多个扩展名的文件,请使用以下文件:

/// <summary>
    /// Get the file with a specific name and extension
    /// </summary>
    /// <param name="filename">the name of the file to find</param>
    /// <param name="extensionsToCompare">string list of all the extensions</param>
    /// <param name="Location">string of the location</param>
    /// <returns>file with the requested filename</returns>
    public string GetFile( string filename, List<string> extensionsToCompare, string Location)
    {
        foreach (string file in Directory.GetFiles(Location))
        {
            if (extensionsToCompare.Contains(file.Substring(file.IndexOf('.') + 1).ToLower()) &&& file.Substring(Location.Length + 1, (file.IndexOf('.') - (Location.Length + 1))).ToLower() == filename) 
                return file;
        }
        return "";
    }


例如调用如下函数:

string image = GetFile("imagename", new List<string>{"jpg", "png", "gif"}, "imageFolder");


#23 楼

这是获取过滤文件的简单而优雅的方法

var allowedFileExtensions = ".csv,.txt";


var files = Directory.EnumerateFiles(@"C:\MyFolder", "*.*", SearchOption.TopDirectoryOnly)
                .Where(s => allowedFileExtensions.IndexOf(Path.GetExtension(s)) > -1).ToArray(); 


#24 楼

我不知道哪种解决方案更好,但是我使用以下方法:

String[] ext = "*.ext1|*.ext2".Split('|');

            List<String> files = new List<String>();
            foreach (String tmp in ext)
            {
                files.AddRange(Directory.GetFiles(dir, tmp, SearchOption.AllDirectories));
            }


#25 楼

使用GetFiles搜索模式过滤扩展名是不安全的!
例如,您有两个文件Test1.xls和Test2.xlsx,并且您想使用搜索模式* .xls过滤出xls文件,但是GetFiles都返回了Test1 .xls和Test2.xlsx
我没有意识到这一点,并且在生产环境中出现错误,因为一些临时文件突然被当作正确的文件处理。搜索模式为* .txt,临时文件的名称为* .txt20181028_100753898
,因此无法信任搜索模式,还必须对文件名添加额外的检查。

评论


不回答问题。

–罗比·科恩(Robbie Coyne)
19年11月18日在1:50

#26 楼

或者,您可以仅将扩展字符串转换为String ^

vector <string>  extensions = { "*.mp4", "*.avi", "*.flv" };
for (int i = 0; i < extensions.size(); ++i)
{
     String^ ext = gcnew String(extensions[i].c_str());;
     String^ path = "C:\Users\Eric\Videos";
     array<String^>^files = Directory::GetFiles(path,ext);
     Console::WriteLine(ext);
     cout << " " << (files->Length) << endl;
}


评论


这是C ++而不是C#

–括号
17年7月12日在16:35