使用Delphi快速搜索大型文件中是否存在字符串

lke*_*ler 4 delphi optimization search large-files

我的程序中有一个FindFile例程,它将列出文件,但是如果填写了"Containing Text"字段,那么它应该只列出包含该文本的文件.

在此输入图像描述

如果输入"包含文本"字段,则我搜索为文本找到的每个文件.我目前的做法是:

  var
    FileContents: TStringlist;

  begin
    FileContents.LoadFromFile(Filepath);
    if Pos(TextToFind, FileContents.Text) = 0 then
      Found := false
    else 
      Found := true;
Run Code Online (Sandbox Code Playgroud)

上面的代码很简单,一般都可以.但它有两个问题:

  1. 对于非常大的文件(例如300 MB),它会失败

  2. 我觉得它可能会更快.这还不错,但为什么要等待10分钟搜索1000个文件,如果可能有一个简单的方法来加快它的速度?

我需要这个用于Delphi 2009并搜索可能是也可能不是Unicode的文本文件.它只需要用于文本文件.

那么如何加快搜索速度并使其适用于非常大的文件呢?


额外奖励:我还想要允许"忽略大小写"选项.提高效率是一件更艰难的事情.有任何想法吗?


解:

嗯,mghie指出我之前的问题如何在Delphi中有效地阅读许多文件的前几行,而且正如我所回答的那样,它是不同的,并没有提供解决方案.

但是他让我想到我以前做过这件事而且我曾经做过.我为大文件构建了一个块读取例程,将其分解为32 MB块.我用它来读取我的程序的输入文件,这可能是巨大的.该例程工作正常,速度快.所以第一步是对我正在浏览的这些文件做同样的事情.

所以现在问题是如何在这些块内有效搜索.那么我确实有一个关于该主题的上一个问题:Delphi中是否有一个高效的全字搜索功能?和RRUZ向我指出了SearchBuf例程.

这也解决了"奖金",因为SearchBuf有选项,包括全字搜索(该问题的答案)和MatchCase/noMatchCase(奖金的答案).

所以我开始跑步了.再次感谢SO社区.

Tho*_*ler 12

这里最好的方法可能是使用内存映射文件.

首先,您需要一个文件句柄,使用CreateFileWindows API函数.

然后传递给它CreateFileMapping来获取文件映射句柄.最后用于MapViewOfFile将文件映射到内存中.

要处理大型文件,MapViewOfFile只能将某个范围映射到内存中,因此您可以例如映射前32MB,然后使用UnmapViewOfFile取消映射,然后使用MapViewOfFile下一个32MB,依此类推.(编辑:如下所述,确保您以这种方式映射的块重叠4kb的倍数,并且至少与您要搜索的文本的长度重叠,这样您就不会忽略任何文本可能会在区块边界处分裂)

要在文件(的一部分)映射到内存后进行实际搜索,您可以StrPosLen从SysUtils.pas 复制源代码(不幸的是,它只在实现部分中定义,而不是在接口中公开).保留一份副本是,让另一个副本,更换WideAnsi每次.此外,如果您希望能够搜索可能包含嵌入式文件的二进制文件#0,则可以删除该(Str1[I] <> #0) and部件.

找到一种方法来识别文件是ANSI还是Unicode,或者只是在文件的每个映射部分调用Ansi和Unicode版本.

完成每个文件后,请确保先调用CloseHandle文件映射句柄,然后再调用文件处理.(别忘了先打电话UnmapViewOfFile).

编辑:

使用内存映射文件而不是使用例如TFileStream以块的形式将文件读入内存的一大优点是字节只会在内存中结束一次.

通常,在文件访问时,首先Windows将字节读入OS文件缓存.然后将它们从那里复制到应用程序内存中.

如果使用内存映射文件,操作系统可以直接将OS文件缓存中的物理页映射到应用程序的地址空间,而无需另外复制(减少了复制所需的时间和内存使用量的减半).

奖励答案:通过调用StrLIComp而不是StrLComp,您可以进行不区分大小写的搜索.

  • 对于以小块(例如32 MB)分块的内存映射文件,如果此文本在两个32 MB缓冲区之间分割,则会松散以识别某些文本内容.实施必须照顾到这一点!实际上,如果您必须从头到尾遍历所有文件内容,那么32 MB内存映射文件不会比普通TFileStream读入固定32 MB缓冲区更快.您从文件缓存中谈到的内存副本不会产生任何重大差异:瓶颈将在StrILComp()中,而不是在此内存副本中. (2认同)

mgh*_*hie 3

这是与您之前的问题How Can I Efficiently Read The First Few Lines of Many Files in Delphi相关的问题,并且适用相同的答案。如果您不完全读取文件而是按块读取文件,那么大文件不会造成问题。对于包含文本的文件来说,速度也有很大的提高,因为您应该在第一次匹配时取消搜索。目前,即使要找到的文本位于前几行,您也会读取整个文件。

  • 如果您特别关心的话,您可以使用 Boyer-Moore 搜索,但与不从磁盘加载超出需要的内容相比,它的速度仅稍快一些。如果您正在计算已加载文件中的匹配项,那么它会非常方便。 (3认同)