代码之家  ›  专栏  ›  技术社区  ›  adeel825

C#中最快的方法是在包含20000多个文件的目录中查找文件

  •  18
  • adeel825  · 技术社区  · 16 年前

    我有一个每天晚上运行的任务,从根目录下有20000多个子文件夹的目录中提取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文件。 我只知道根文件夹和xml目录的名称。

    下面的代码遍历所有目录,速度非常慢。有没有关于如何优化搜索的建议,特别是在已知目录结构的情况下?

    string[] files = Directory.GetFiles(@"\\somenetworkpath\rootFolder", "*.xml", SearchOption.AllDirectories);
    
    9 回复  |  直到 16 年前
        1
  •  17
  •   Mitchel Sellers    16 年前

    与使用GetFiles和蛮力搜索相比,您最有可能使用GetDirectory,首先获取“第一个子文件夹”的列表,遍历这些目录,然后对子文件夹重复该过程,遍历它们,最后查找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();
    

    如果你把它放到一个测试控制台应用程序中,你会看到它输出结果。

        2
  •  6
  •   Julián Urbano    11 年前

    我创建了一个递归方法 GetFolders 使用 Parallel.ForEach 查找名为变量的所有文件夹 yourKeyword

    List<string> returnFolders = new List<string>();
    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;
    
        3
  •  3
  •   Adam Robinson    16 年前

            System.IO.DirectoryInfo root = new System.IO.DirectoryInfo(rootPath);
            List<System.IO.FileInfo> xmlFiles=new List<System.IO.FileInfo>();
    
            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"));
                    }
                }
            }
    
        4
  •  0
  •   Chris Doggett    16 年前

    在C#中我想不出任何更快的方法,但是您是否为该文件系统启用了索引功能?

        5
  •  0
  •   Michael    16 年前

    我能看出这会有很大不同的唯一方法是从蛮力狩猎转变为使用第三方或操作系统索引例程来加速返回。这样,搜索就可以从你的应用程序离线完成。

    但我也建议,如果可能的话,你应该寻找更好的方法来构建这些数据。

        6
  •  0
  •   Richard    16 年前

    在上使用P/Invoke FindFirstFile / FindNextFile FindClose 并且避免了创建大量FileInfo实例的开销。

        7
  •  0
  •   Henrik Gering    7 年前

    根据您的需要和配置,您可以使用Windows搜索索引: https://msdn.microsoft.com/en-us/library/windows/desktop/bb266517(v=vs.85).aspx

    根据您的配置,这可能会大大提高性能。

        8
  •  0
  •   VladVS    7 年前

    您可以在GitHub上找到有关库的所有信息: https://github.com/VladPVS/FastSearchLibrary https://github.com/VladPVS/FastSearchLibrary/releases 如果你有任何问题,请问他们。

    工作非常快。你自己检查一下!

    class Searcher
    {
        private static object locker = new object(); 
    
        private FileSearcher searcher;
    
        List<FileInfo> files;
    
        public Searcher()
        {
            files = new List<FileInfo>();
        }
    
        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<string> folders = new List<string>
    {
      @"C:\Users\Public",
      @"C:\Windows\System32",
      @"D:\Program Files",
      @"D:\Program Files (x86)"
    }; // list of search directories
    
    List<string> keywords = new List<string> { "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<FileInfo> files = FileSearcher.GetFilesFast(@"C:\Users", "*.xml");
    

    此外,如果使用多核处理器,该库的快速方法的执行速度至少是简单的单线程递归算法的2倍。

        9
  •  0
  •   Waheed Rafiq    7 年前

        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