在C#中以最快的方式在超过20,000个文件的目录中查找文件
我有一个每晚都要运行的工作,从根目录下有超过20,000个子文件夹的目录中提取xml文件。 这是结构的样子:
rootFolder/someFolder/someSubFolder/xml/myFile.xml rootFolder/someFolder/someSubFolder1/xml/myFile1.xml rootFolder/someFolder/someSubFolderN/xml/myFile2.xml rootFolder/someFolder1 rootFolder/someFolderN
所以看看上面的结构,结构总是一样的 – 一个根文件夹,然后是两个子文件夹,然后是一个xml目录,然后是xml文件。 我只知道rootFolder和xml目录的名称。
下面的代码遍历所有目录,速度极慢。 有关如何优化搜索的任何建议,尤其是目录结构是否已知?
string[] files = Directory.GetFiles(@"\\somenetworkpath\rootFolder", "*.xml", SearchOption.AllDirectories);
你可以使用GetDirectories来获取“第一个子文件夹”列表,循环浏览这些目录,然后重复子文件夹的循环,循环遍历它们,最后看看,而不是使用GetFiles并进行powershell搜索。对于xml文件夹,最后搜索.xml文件。
现在,至于性能,速度会有所不同,但首先搜索目录,然后获取文件应该有很多帮助!
更新
好的,我做了一些测试,你实际上可以比我想象的更进一步优化它。
以下代码段将搜索目录结构并在整个目录树中查找所有“xml”文件夹。
string startPath = @"C:\Testing\Testing\bin\Debug"; string[] oDirectories = Directory.GetDirectories(startPath, "xml", SearchOption.AllDirectories); Console.WriteLine(oDirectories.Length.ToString()); foreach (string oCurrent in oDirectories) Console.WriteLine(oCurrent); Console.ReadLine();
如果将其放入测试控制台应用程序,您将看到它输出结果。
现在,一旦你有了这个,只需查看每个找到的.xml文件目录。
我使用Parallel.ForEach
创建了一个递归方法GetFolders
来查找名为变量yourKeyword
所有文件夹
List returnFolders = new List (); object locker = new object(); Parallel.ForEach(subFolders, subFolder => { if (subFolder.ToUpper().EndsWith(yourKeyword)) { lock (locker) { returnFolders.Add(subFolder); } } else { lock (locker) { returnFolders.AddRange(GetFolders(Directory.GetDirectories(subFolder))); } } }); return returnFolders;
是否有与xml文件夹相同级别的其他目录? 如果是这样,如果您自己进行搜索并从搜索中消除该级别,则可能会加快搜索速度。
System.IO.DirectoryInfo root = new System.IO.DirectoryInfo(rootPath); List xmlFiles=new List(); foreach (System.IO.DirectoryInfo subDir1 in root.GetDirectories()) { foreach (System.IO.DirectoryInfo subDir2 in subDir1.GetDirectories()) { System.IO.DirectoryInfo xmlDir = new System.IO.DirectoryInfo(System.IO.Path.Combine(subDir2.FullName, "xml")); if (xmlDir.Exists) { xmlFiles.AddRange(xmlDir.GetFiles("*.xml")); } } }
在C#中我想不出更快的事情,但你是否为该文件系统打开了索引?
只有这样我才能看到会产生很大影响的方法是从powershell搜索中改变并使用一些第三方或操作系统索引例程来加速返回。 这样,搜索就会从你的应用程序离线完成。
但我还建议你应该考虑更好的方法来构建数据,如果可能的话。
在FindFirstFile
/ FindNextFile
/ FindClose
上使用P / Invoke,避免创建大量FileInfo实例的开销。
但要做到这一点很难(你必须自己完成文件与目录和递归的所有处理)。 所以尝试一些简单的东西(Directory.GetFiles(),Directory.GetDirectories())来开始并使事情有效。 如果它太慢,请查看替代方案(但总是测量,太容易使其变慢)。
根据您的需求和配置,您可以使用Windows搜索索引: https : //msdn.microsoft.com/en-us/library/windows/desktop/bb266517(v = vs。85)。aspx
根据您的配置,这可以大大提高性能。
对于文件和目录搜索目的,我想提供使用具有广泛搜索机会的multithreading.NET库。 有关您可以在GitHub上找到的库的所有信息: https : //github.com/VladPVS/FastSearchLibrary如果您想下载它,您可以在此处进行: https : //github.com/VladPVS/FastSearchLibrary/releases如果您有任何问题请问他们。
工作得非常快。 自己检查一下!
这是一个如何使用它的示范性示例:
class Searcher { private static object locker = new object(); private FileSearcher searcher; List files; public Searcher() { files = new List (); } public void Startsearch() { CancellationTokenSource tokenSource = new CancellationTokenSource(); searcher = new FileSearcher(@"C:\", (f) => { return Regex.IsMatch(f.Name, @".*[Dd]ragon.*.jpg$"); }, tokenSource); searcher.FilesFound += (sender, arg) => { lock (locker) // using a lock is obligatorily { arg.Files.ForEach((f) => { files.Add(f); Console.WriteLine($"File location: {f.FullName}, \nCreation.Time: {f.CreationTime}"); }); if (files.Count >= 10) searcher.StopSearch(); } }; searcher.SearchCompleted += (sender, arg) => { if (arg.IsCanceled) Console.WriteLine("Search stopped."); else Console.WriteLine("Search completed."); Console.WriteLine($"Quantity of files: {files.Count}"); }; searcher.StartSearchAsync(); } }
这是其他例子的一部分:
*** List folders = new List { @"C:\Users\Public", @"C:\Windows\System32", @"D:\Program Files", @"D:\Program Files (x86)" }; // list of search directories List keywords = new List { "word1", "word2", "word3" }; // list of search keywords FileSearcherMultiple multipleSearcher = new FileSearcherMultiple(folders, (f) => { if (f.CreationTime >= new DateTime(2015, 3, 15) && (f.Extension == ".cs" || f.Extension == ".sln")) foreach (var keyword in keywords) if (f.Name.Contains(keyword)) return true; return false; }, tokenSource, ExecuteHandlers.InCurrentTask, true); ***
而且可以使用简单的静态方法:
List files = FileSearcher.GetFilesFast(@"C:\Users", "*.xml");
请注意,此库的所有方法都不会抛出UnauthorizedAccessException而是标准的.NET搜索方法。
此外,如果使用多核处理器,此库的快速方法至少比简单的单线程递归算法快2倍。
对于那些想要搜索单个文件并且您知道根目录的人,我建议您尽可能保持简单。 这种方法对我有用。
private void btnSearch_Click(object sender, EventArgs e) { string userinput = txtInput.Text; string sourceFolder = @"C:\mytestDir\"; string searchWord = txtInput.Text + ".pdf"; string filePresentCK = sourceFolder + searchWord; if (File.Exists(filePresentCK)) { pdfViewer1.LoadFromFile(sourceFolder+searchWord); } else if(! File.Exists(filePresentCK)) { MessageBox.Show("Unable to Find file :" + searchWord); } txtInput.Clear(); }// end of btnSearch method