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

时间:2009-04-03 14:16:37

标签: c# .net file-io

我有一个每晚都要运行的作业,从根目录下有超过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);

9 个答案:

答案 0 :(得分:16)

而不是使用GetFiles并进行强力搜索,你很可能使用GetDirectories,首先获取“第一个子文件夹”的列表,循环遍历这些目录,然后重复子文件夹的进程,循环遍历它们,最后查找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文件。

答案 1 :(得分:6)

我使用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;

答案 2 :(得分:3)

是否有与xml文件夹处于同一级别的其他目录?如果是这样,如果您自己进行搜索,可能会加快搜索速度并从搜索中消除该级别。

        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"));
                }
            }
        }

答案 3 :(得分:0)

我无法想到C#中的任何内容,但您是否为该文件系统启用了索引?

答案 4 :(得分:0)

只有这样我才能看到会产生很大影响的方法是从强力搜索中改变并使用某些第三方或操作系统索引例程来加速返回。这样,搜索就会从你的应用程序离线完成。

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

答案 5 :(得分:0)

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

但要做到这一点很难(你必须自己完成文件与目录和递归的所有处理)。所以尝试一些简单的东西(Directory.GetFiles(),Directory.GetDirectories())来开始并使事情正常工作。如果它太慢,请查看替代方案(但总是测量,太容易使其变慢)。

答案 6 :(得分:0)

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

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

答案 7 :(得分:0)

对于文件和目录搜索目的,我想提供使用具有广泛搜索机会的多线程.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<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");

请注意,此库的所有方法都不会抛出UnauthorizedAccessException而是标准的.NET搜索方法。

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

答案 8 :(得分:0)

对于那些想要搜索单个文件并且您知道根目录的人,我建议您尽可能保持简单。这种方法对我有用。

    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