C# 高效地检索和筛选文件
本文讨论如何检索目录树中与多个扩展名之一匹配的所有文件 例如,检索C:\和所有子目录中的所有文件,匹配*.log、*.txt、*.dat 公认的答案是:C# 高效地检索和筛选文件,c#,linq,performance,file,C#,Linq,Performance,File,本文讨论如何检索目录树中与多个扩展名之一匹配的所有文件 例如,检索C:\和所有子目录中的所有文件,匹配*.log、*.txt、*.dat 公认的答案是: var files = Directory.GetFiles("C:\\path", "*.*", SearchOption.AllDirectories) .Where(s => s.EndsWith(".mp3") || s.EndsWith(".jpg")); 这让我觉得效率很低。如果在包含数千个文件的目录
var files = Directory.GetFiles("C:\\path", "*.*", SearchOption.AllDirectories)
.Where(s => s.EndsWith(".mp3") || s.EndsWith(".jpg"));
这让我觉得效率很低。如果在包含数千个文件的目录树上搜索(它使用SearchOption.AllDirectory),则指定目录树中的每个文件都会加载到内存中,只有在该目录树中,不匹配才会被删除。(让我想起了ASP.NET datagrids提供的“分页”功能。)
不幸的是,标准的System.IO.DirectoryInfo.GetFiles方法一次只接受一个筛选器
这可能是因为我缺乏Linq知识,我提到的方式是否真的效率低下
其次,有没有一种更有效的方法可以在使用Linq和不使用Linq的情况下(无需多次调用GetFiles)执行此操作?关于内存消耗,您是对的。然而,我认为这是一个相当不成熟的优化。加载几千个字符串的数组根本没有问题,无论是性能还是内存消耗都没有问题。然而,读取包含这么多文件的directoy是一件非常困难的事情,不管您如何存储/过滤文件名:它总是相对缓慢。创建自己的目录遍历函数并使用 编辑:我做了一个简单的测试,我不知道它是否正是你需要的
class Program
{
static string PATH = "F:\\users\\llopez\\media\\photos";
static Func<string, bool> WHERE = s => s.EndsWith(".CR2") || s.EndsWith(".html");
static void Main(string[] args)
{
using (new Profiler())
{
var accepted = Directory.GetFiles(PATH, "*.*", SearchOption.AllDirectories)
.Where(WHERE);
foreach (string f in accepted) { }
}
using (new Profiler())
{
var files = traverse(PATH, WHERE);
foreach (string f in files) { }
}
Console.ReadLine();
}
static IEnumerable<string> traverse(string path, Func<string, bool> filter)
{
foreach (string f in Directory.GetFiles(path).Where(filter))
{
yield return f;
}
foreach (string d in Directory.GetDirectories(path))
{
foreach (string f in traverse(d, filter))
{
yield return f;
}
}
}
}
class Profiler : IDisposable
{
private Stopwatch stopwatch;
public Profiler()
{
this.stopwatch = new Stopwatch();
this.stopwatch.Start();
}
public void Dispose()
{
stopwatch.Stop();
Console.WriteLine("Runing time: {0}ms", this.stopwatch.ElapsedMilliseconds);
Console.WriteLine("GC.GetTotalMemory(false): {0}", GC.GetTotalMemory(false));
}
}
GetFiles方法只读取文件名,而不读取文件内容,因此,虽然读取所有名称可能是浪费,但我不认为这有什么好担心的
据我所知,唯一的替代方法是执行多个GetFiles调用并将结果添加到集合中,但这会变得很笨拙,需要您扫描文件夹几次,因此我怀疑它也会变慢 我分享了你的问题,并在Matthew Podwysocki的at中找到了解决方案 他使用本机方法实现了一个解决方案,该方法允许您在他的GetFiles实现中提供谓词。此外,他使用yield语句实现了他的解决方案,有效地将每个文件的内存利用率降至绝对最低 使用他的代码,您可以编写如下内容:
var allowedExtensions = new HashSet<string> { ".jpg", ".mp3" };
var files = GetFiles(
"C:\\path",
SearchOption.AllDirectories,
fn => allowedExtensions.Contains(Path.GetExtension(fn))
);
var allowedExtensions=newhashset{.jpg',.mp3};
var files=GetFiles(
“C:\\path”,
SearchOption.AllDirectories,
fn=>allowedExtensions.Contains(Path.GetExtension(fn))
);
files变量将指向一个枚举器,该枚举器返回匹配的文件(延迟执行样式)。对于大量文件,理想的做法是GetFiles()接受多个过滤器,然后逐个文件遍历整个目录树(无论如何都是这样),为每个匹配调用提供的方法。好的方面。我认为这可能会帮助你避免加载所有的文件名,并且只在需要时检索这些值。
var allowedExtensions = new HashSet<string> { ".jpg", ".mp3" };
var files = GetFiles(
"C:\\path",
SearchOption.AllDirectories,
fn => allowedExtensions.Contains(Path.GetExtension(fn))
);