Delphi搜索文件和目录最快的算法

时间:2010-06-16 15:02:18

标签: delphi file search

我正在使用Delphi7,我需要一个解决一个大问题的解决方案。有人为我提供了一种比使用findnext和findfirst更快的搜索文件和文件夹的方法吗?因为我也处理每个文件/文件夹的数据(创建日期/作者/大小/等),这需要很多时间...我在WinApi下搜索了很多但可能我没有看到最好的功能为了实现这一目标。我在Delphi中发现的所有例子都使用了findfirst和findnext ...

另外,我不想购买组件或使用一些免费的组件......

提前致谢!

10 个答案:

答案 0 :(得分:7)

我认为您购买的任何组件也会使用findfirst / findnext。当然是递归的。我认为没有办法查看每个目录和文件,而不实际查看每个目录和文件。

作为查看代码是否相当快的基准,请将性能与WinDirStat http://windirstat.info/进行比较(只是收集数据的时间点,并准备构建其空间使用情况图。)
如果您想查看他们正在做什么,可以使用源代码。它是C,但我希望它使用相同的API调用。

答案 1 :(得分:6)

如果您的卷是NTFS,那么您可以做的最重要的事情就是直接解析MFT。通过这样做,您可以非常快速地枚举文件,非常 - 我们谈论的速度至少要快一个数量级。如果您需要的所有元数据都是MFT记录的一部分,那么您的搜索将更快完成。即使您需要对额外元数据进行更多读取,您也可以非常快速地建立候选文件列表。

缺点是你必须自己解析MFT:我知道没有WinAPI功能。你也会担心shell通常会担心硬链接,连接点,重新分析点,符号链接,shell链接等等。

但是,如果你想要速度,复杂性的增加是实现它的唯一方法。

我不知道任何可用的Delphi代码已经实现了MFT解析器,因此您可能必须使用第三方库或自己实现它。我打算建议使用Delphi编写的开源(GPL)NTFS Undelete,但它通过Python代码实现MFT解析,并内置了Delphi-Python桥。

答案 2 :(得分:2)

如果您想获得非常快的搜索结果,请考虑使用Windows搜索(API)或索引服务。

其他改进可能是利用线程并拆分搜索文件和收集文件属性,或者只是进行线程搜索。

答案 3 :(得分:1)

如果你需要使用那么多文件扫描远程驱动器,我强烈建议使用“客户端 - 服务器”设计,这样实际的文件扫描总是在本地完成,只有远程获取结果。那会节省你很多时间。此外,所有“服务器”都可以并行扫描。

答案 4 :(得分:1)

我遇到过一个非常类似的问题,目录中的文件数量加上findfirst / findnext花费的时间比合理的多。使用一些文件不是问题,但随着您向上扩展到数千或数万个文件,性能会大幅下降。

我们的解决方案是在单独的目录中使用队列文件。当文件被“添加”到系统时,它们被写入队列文件(是一个固定的记录文件)。当系统需要处理数据时,它将查看文件是否存在,如果存在,则重命名并打开重命名的版本(这样可以在下一个进程传递时添加)。然后按顺序处理该文件。然后我们归档了队列文件&根据日期和时间将文件处理到子目录中(例如:G:\ PROCESSED \ 2010 \ 06 \ 25 \ 1400包含2010年6月25日下午2:00运行的文件)。

使用这种方法,我们不仅实现了对文件的几乎“实时”处理(仅按我们处理队列文件的频率延迟),而且我们还按照添加顺序保证文件处理。

答案 5 :(得分:1)

如果您的程序在Windows 7或Server 2008 R2上运行,则对Windows FindFirstFileEx功能进行一些增强,这将使其运行速度更快一些。您必须复制和修改VCL函数以合并新选项。

答案 6 :(得分:1)

使用findfirst / findnext循环进行优化的空间不大,因为它主要受I / O限制:操作系统需要从硬盘中读取此信息!

证明:创建一个小程序,实现一个简单的findfirst / findnext循环,它对找到的文件没有任何影响。重新启动计算机并在大目录上运行它,记下完成所需的时间。然后再次运行它,而无需重新启动计算机。您会注意到第二次运行速度明显加快,因为操作系统缓存了信息!

如果您确定操作系统会严重访问您尝试扫描的目录,因为某些其他应用程序正在使用该数据(这会将目录结构信息放入操作系统的缓存中并使扫描不受限制I / O)你可以尝试使用线程并行运行几个findfirst / findnext循环。这样做的缺点是如果目录结构还没有完全在操作系统缓存中,那么你的算法再次绑定到HDD输入/输出,它可能比原来的更差,因为你现在正在制作多个需要的并行I / O请求由同一设备处理。

当我不得不解决同样的问题时,我决定不采用并行循环,因为应用程序的第二次运行总是如此快得多,推测我必须接受I / O并且没有大量的CPU优化会修复I / O瓶颈。

答案 7 :(得分:1)

我通过使用两个线程解决了类似的问题。这样我就可以在从磁盘扫描的同时“处理”文件。在我的情况下,处理速度明显慢于扫描,所以我还必须一次限制内存中的文件数。

<强> TMyScanThread

扫描文件结构,对于每个“命中”,使用Syncronize()将路径+文件添加到TList / TStringList或类似文件。记得在循环中使用Sleep()让操作系统也有一些时间。

线程的伪代码:

TMyScanThread=class(TThread)
private
  fCount : Cardinal;
  fLastFile : String;
  procedure GetListCount;
  procedure AddToList;  
public
  FileList : TStringList;
  procedure Execute; Override;
end;

procedure TMyScanThread.GetListCount;
begin
  fCount := FileList.Count;
end;

procedure TMyScanThread.AddToList;
begin
  FileList.Add(fLastFile);
end;

procedure TMyScanThread.Execute;
begin
     try
        { Get the list size }
        Syncronize( GetListCount );
        if fCount<500 then
        begin
          // FindFirst code goes here
          { Add a file to the list }
          fLastFile := SR.Name; // Store Filename in local var
          Syncronize( AddToList ); // Call method to add to list
          SleepEx(0,True);
        end else
          SleepEx(1000,True);
     finally
        Terminate;
     end;
end;

<强> TMyProcessFilesThread

获取列表中最旧的条目,然后处理它。然后将结果输出到DB。

此类与使用访问列表的Syncronized方法类似地实现。

Syncronize()调用的一种替代方法是使用TCriticalSection。在线程之间实现Syncron化通常是一种品味和手头的任务......

答案 8 :(得分:1)

答案 9 :(得分:0)

当我开始遇到在文件系统中处理大量小文件的性能问题时,我转而将文件存储为数据库中的blob。没有理由不能将大小,创建和作者等相关信息存储在数据库中。一旦表填充在数据库中,我怀疑数据库引擎可以比我们想要提出的任何解决方案更快地找到记录(文件),因为数据库代码高度专业化,可以高效地搜索大数据集。这肯定会更灵活,因为添加新搜索就像创建新的Select语句一样简单。示例:从author ='bob'和size&gt;的文件中选择* 10000

我不确定这种方法会对你有所帮助。您能告诉我们更多关于您使用这些文件以及搜索条件的信息。