English 中文(简体)
使用Delphi快速搜索大型文件中是否存在字符串
原标题:Fast Search to see if a String Exists in Large Files with Delphi

我的程序中有一个FindFile例程,它会列出文件,但如果填写了“包含文本”字段,那么它应该只列出包含该文本的文件。

如果输入了“包含文本”字段,那么我会在找到的每个文件中搜索文本。我目前的做法是:

  var
    FileContents: TStringlist;

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

上面的代码很简单,而且通常工作正常。但它有两个问题:

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

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

我需要这个来为Delphi 2009工作,并搜索可能是Unicode也可能不是Unicode的文本文件。它只需要适用于文本文件。

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


额外的好处:我还想允许一个“忽略案例”选项。这是一个更难提高效率的问题。有什么想法吗?


解决方案:

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

但他让我觉得我以前也做过这件事。我为大文件构建了一个块读取例程,将其分解为32 MB的块。我用它来读取我的程序的输入文件,这可能是巨大的。这个程序运行得又好又快。所以第一步是对我正在查看的这些文件做同样的操作。

所以现在的问题是如何在这些区块内有效地进行搜索。我之前确实有一个关于这个主题的问题:Delphi中有一个高效的全词搜索函数吗?和RRUZ向我指出了SearchBuf例程。

这也解决了“奖金”,因为SearchBuf有包括全词搜索(该问题的答案)和MatchCase/noMatchCase(奖金的答案)在内的选项。

所以我要跑了。再次感谢SO社区。

最佳回答

这是一个与您之前的问题有关的问题How Can I Efficiently Read The First Few Lines of Many Files in Delphi,同样的答案也适用。如果你没有完全阅读文件,而是分块阅读,那么大文件就不会造成问题。对于包含文本的文件,也有很大的加速,因为你应该在第一次匹配时取消搜索。目前,即使要找到的文本位于他写了几行字。

问题回答

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

首先需要一个文件句柄,使用CreateFilewindows API函数即可。

然后将其传递给CreateFileMapping以获得文件映射句柄。最后使用MapViewOfFile将文件映射到内存中。

要处理大文件,MapViewOfFile只能将某个范围映射到内存中,因此您可以例如映射前32MB,然后使用UnmapViewOfFile取消映射,然后为接下来的32MB使用MapViewOfFile[/code>,依此类推

要在文件(的一部分)映射到内存后进行实际搜索,您可以从SysUtils.pas中复制StrPosLen的源代码(不幸的是,它仅在实现部分中定义,未在接口中公开)。保持一个副本不变,然后制作另一个副本,每次将Wide替换为Ansi。此外,如果您希望能够在可能包含嵌入式#0的二进制文件中进行搜索,则可以删除(Str1[I]<;>;#0)和部分。

找到一种方法来识别文件是ANSI还是Unicode,或者简单地对文件的每个映射部分调用ANSI和Unicode版本。

处理完每个文件后,请确保首先在文件映射句柄上调用CloseHandle,然后在文件处理上调用。(不要忘记先调用<code>UnmapViewOfFile</code>)。

编辑:

使用内存映射文件而不是使用例如TFileStream将文件分块读取到内存中的一大优势是,字节只会在内存中结束一次。

通常,在文件访问时,首先Windows会将字节读取到操作系统文件缓存中。然后将它们从那里复制到应用程序内存中。

如果使用内存映射文件,操作系统可以将物理页面从操作系统文件缓存直接映射到应用程序的地址空间,而无需再进行一次复制(减少了进行复制所需的时间,并将内存使用量减半)。

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

如果您正在寻找文本字符串搜索,请寻找Boyer-Moore搜索算法。它使用内存映射文件和非常快速的搜索引擎。是一些包含此算法实现的delphi单元。

为了让你了解速度,我目前搜索10-20MB的文件,所需时间以毫秒为单位。

哦,只是读到它可能是unicode——不确定它是否支持它——但一定要沿着这条路往下看。

May I suggest a component ? If yes I would recommend ATStreamSearch. It handles ANSI and UNICODE (and even EBCDIC and Korean and more).

或者JclUnicode(Jedi jcl)中的类TUTBMSearch。它主要由Mike Lischke(VirtualTreeview)撰写。它使用了经过调整的Boyer-Moore算法来确保速度。在您的情况下,坏的一点是,它完全适用于unicode(宽字符串),因此从字符串到宽字符串的转换可能会受到惩罚。

这取决于你要用它搜索什么样的数据,为了获得真正有效的结果,你需要让你的程序解析有趣的目录,包括其中的所有文件,并将数据保存在一个数据库中,你可以每次访问该数据库中的一个特定单词,该列表中的特定单词可以生成到搜索路径。Database语句可以以毫秒为单位提供结果。

问题是,安装后必须让它运行并解析所有文件,这可能需要超过1个小时的时间才能解析到您想要解析的数据量。

这个数据库应该在每次程序启动时更新,这可以通过比较每个文件的MD5值来完成,如果它被更改了,所以你不必每次都解析所有文件。

如果这种工作方式很有趣,如果你把所有的数据都放在一个固定的地方,并且你在同一个文件中分析数据的次数比每次都是全新的文件要多,那么一些代码分析器就是这样工作的,它们真的很高效。因此,您可以在分析和保存搜索数据上投入一些时间,然后跳到搜索词出现的确切位置,并在很短的时间内提供它出现的所有位置的列表。

如果要多次搜索文件,最好使用单词索引。

这被称为“全文搜索”。

第一次搜索速度会慢一些(必须解析文本并创建索引),但未来的任何搜索都将是即时的:简而言之,它将只使用索引,而不会再次读取所有文本。

You have the exact parser you need in The Delphi Magazine Issue 78, February 2002: "Algorithms Alfresco: Ask A Thousand Times Julian Bucknall discusses word indexing and document searches: if you want to know how Google works its magic this is the page to turn to."

Delphi有几个FTS实现:

I d like to add that most DB have an embedded FTS engine. SQLite3 even has a very small but efficient implementation, with page ranking and such. We provide direct access from Delphi, with ORM classes, to this Full Text Search engine, named FTS3/FTS4.





相关问题
determining the character set to use

my delphi 2009 app has a basic translation system that uses GNUGetText. i had used some win API calls to prepare the fonts. i thought it was working correctly until recently when someone from Malta ...

Help with strange Delphi 5 IDE problems

Ok, I m going nuts here. For the last (almost) four years, I ve been putting up with some extremely bad behavior from my Delphi 5 IDE. Problems include: Seemingly random errors in coride50.bpl ...

How to write a Remote DataModule to run on a linux server?

i would like to know if there are any solution to do this. Does anyone? The big picture: I want to access data over the web, using my delphi thin clients. But i´would like to keep my server/service ...

How convert string to integer in Oxygene

In Delphi, there is a function StrToInt() that converts a string to an integer value; there is also IntToStr(), which does the reverse. These functions doesn t appear to be part of Oxygene, and I can ...

Quick padding of a string in Delphi

I was trying to speed up a certain routine in an application, and my profiler, AQTime, identified one method in particular as a bottleneck. The method has been with us for years, and is part of a "...