在C#中搜索子目录

我有一个文件名列表,我想搜索一个目录及其所有子目录。 这些目录每个包含大约200,000个文件。 我的代码找到了该文件,但每个文件大约需要20分钟。 有人可以提出更好的方法吗?

代码片段

String[] file_names = File.ReadAllLines(@"C:\file.txt"); foreach(string file_name in file_names) { string[] files = Directory.GetFiles(@"I:\pax\", file_name + ".txt", SearchOption.AllDirectories); foreach(string file in files) { System.IO.File.Copy(file, @"C:\" + textBox1.Text + @"\N\O\" + file_name + ".txt" ); } } 

如果要在同一目录结构中搜索多个文件,则应该在该目录结构中找到一次所有文件,然后在内存中搜索它们。 无需一次又一次地访问文件系统。

编辑:有一个优雅的方式,使用LINQ – 和不那么优雅的方式,没有。 这是LINQ方式:

 using System; using System.IO; using System.Linq; class Test { static void Main() { // This creates a lookup from filename to the set of // directories containing that file var textFiles = Directory.GetFiles("I:\\pax", "*.txt", SearchOption.AllDirectories) .ToLookup(file => Path.GetFileName(file), file => Path.GetDirectoryName(file)); string[] fileNames = File.ReadAllLines(@"c:\file.txt"); // Remove the quotes for your real code :) string targetDirectory = "C:\\" + "textBox1.Text" + @"\\N\\O\\"; foreach (string fileName in fileNames) { string tmp = fileName + ".txt"; foreach (string directory in textFiles[tmp]) { string source = Path.Combine(directory, tmp); string target = Path.Combine(targetDirectory, tmp); File.Copy(source, target); } } } } 

如果您需要非LINQ方式,请告诉我。 在我这样做之前要检查一件事 – 这可以将多个文件复制到彼此的顶部。 这真的是你想要做的吗? (想象一下, a.txt存在于多个位置,文件中有“a”。)

您可能最好尝试将所有文​​件路径加载到内存中。 调用Directory.GetFiles()一次,并将结果放入HashSet 。 然后在HashSet上进行查找。 如果你有足够的内存,这将工作正常。 这很容易尝试。

如果内存不足,则必须更聪明,就像使用缓冲区缓存一样。 最简单的方法是将所有文件路径作为行加载到数据库表中,让查询处理器为您管理缓冲区缓存。

这是第一个的代码:

 String[] file_names = File.ReadAllLines(@"C;\file.txt"); HashSet allFiles = new HashSet(); string[] files = Directory.GetFiles(@"I:\pax\", file_name + ".txt", SearchOption.AllDirectories); foreach (string file in files) { allFiles.Add(file); } foreach(string file_name in file_names) { String file = allFiles.FirstOrDefault(f => f == file_name); if (file != null) { System.IO.File.Copy(file, @"C:\" + textBox1.Text + @"\N\O\" + file_name + ".txt"); } } 

通过一次遍历一个目录并将生成的文件数组添加到hashset,您可以更加智能地使用内存。 这样,所有文件名都必须存在于一个大的String []中。

你一遍又一遍地执行一个递归的GetFiles(),它可能是最昂贵的部分。

尝试将所有文​​件加载到内存中,然后对其进行自己的匹配。

请注意,一次加载1个文件夹,并file_name in file_names搜索所有file_name in file_names ,然后对下一个文件夹重复该文件夹,效率会更高。

扫描目录结构是一个IO密集型操作,无论你做什么,第一次GetFiles()调用将花费大部分时间,在第一次调用结束时,大多数文件信息可能会在文件系统缓存和第二次调用中与第一次调用相比,它将立即返回(取决于您的可用内存和文件系统缓存大小)。

可能你最好的选择是打开文件系统的索引并以某种方式使用它; 以编程方式查询索引

一目了然,似乎有.NET API来调用Windows索引服务…如果你正在使用的机器启用了索引(我也不确定前面提到的服务是指XP时代的索引服务还是Windows搜索索引服务)。

谷歌搜索

一个可能的领先

另一个

尝试使用LINQ查询文件系统。 不是100%肯定性能,但它很容易测试。

 var filesResult = from file in new DirectoryInfo(path).GetFiles("*.txt", SearchOption.AllDirectories) where file.Name = filename select file; 

然后用结果做任何你想做的事。

Linq的答案可能会遇到问题,因为它会在开始从中选择之前将所有文件名加载到内存中。 通常,您可能希望一次加载单个目录的内容,以减少内存压力。

但是,对于这样的问题,您可能希望在问题公式中上升一级。 如果这是您经常进行的查询,那么您可以构建一些使用FileSystemListener来监听顶级目录及其下的所有目录的更改。 通过遍历所有目录并将它们构建为Dictionary <>或HashSet <>来启动它。 (是的,这与Linq解决方案存在相同的内存问题)。 然后,当您获得文件添加/删除/重命名修改时,请更新字典。 这样,可以非常快速地回答每个单独的查询。

如果这是来自大量调用的工具的查询,您可能希望将FileSystemWatcher构建到服务中,并从需要知道的实际工具连接到/查询该服务,以便可以构建文件系统信息一次,并在服务过程的生命周期中重复使用。