C++搜索算法-处理海量数据

C++ Search Algorithms - Processing Huge Data

本文关键字:数据 处理海 搜索算法 C++      更新时间:2023-10-16

我有一个代码,可以在文件中搜索字符串,文件大小可以是1mg,也可以是1gg或更大。

我用ReadFile()WinAPI获取文件数据并转换为十六进制,然后在转换的数据中搜索一个字符串(之前是十六进制的)。

我用这个代码进行搜索(字符串搜索):

std::string searchStr = "48656C6C6FA"
std::string fileData = ToHex(inputString);
if(fileData.find(searchStr, 0) != std::string::npos)
{
std::cout << FileName;
}

2900文件中搜索字符串几乎需要11秒。

还有其他更快的搜索算法或功能吗?这种方式(如上所述)有时会漏掉字符串,无法完美工作。

如果您有一个较小的文件(比如几兆字节,甚至几百兆字节,取决于系统的内存量),那么将其全部读取到内存中,否则我建议使用内存映射文件。如果文件太大而无法映射,您可以使用滑动窗口或双缓冲算法将文件中的数据块读取到内存中。

然后,要搜索特定的字节序列,请对文件的内容进行线性搜索,查找搜索序列的第一个字节(在0x48656C6C6FA的情况下,即0xFA)。如果找到,则尝试将序列中的第二个字节(在本例中为0xC6)与文件中的下一个字节进行匹配,依此类推,直到匹配完整个序列。

如果第二个(或连续的)字节不匹配,则继续搜索第一个字节。

这具有O(n)复杂性,其中n是文件中的字节数。除非你事先知道你搜索的数据在文件的特定部分,否则这是你能得到的最好的数据。


如果文件存在于SSD上,则可以使用线程进行搜索,每个文件一个线程但是不是一次所有2900个文件,这将淹没处理器。相反,有4-8个线程进行搜索(取决于系统的内核数量),一旦一个线程处理完一个文件,它就会处理下一个。

不能在旋转的磁盘驱动器上使用,因为当线程试图读取时,磁头来回寻找时,它会颠簸磁盘。

速度:使用内存映射文件

准确性:使用std::使用二进制值进行搜索。

例如

#include <algorithm>
#include <cstdint>
#include <tuple>
#include <vector>
// some function to return a pointer to the first byte in the file and the length 
extern std::tuple<const std::uint8_t*, std::size_t> get_file_bounds();
int main()
{
auto [begin, size] = get_file_bounds();
auto search_string = std::vector<std::uint8_t> {
0x48,
0x65,
0x6C,
0x6C,
0x6F
};
auto iter = std::search(begin, begin + size, 
search_string.begin(), search_string.end());
if (iter != begin + size)
{
// found the sequence 
}
else 
{
// didn't find it
}
}

对于像您拥有的搜索字符串(显然是5 1/2字节)这样短的搜索字符串,瓶颈通常是磁盘I/O。我怀疑那2900个文件可能在硬盘上。这将转化为每个文件大约4毫秒,这是相当不错的。

当然,转换为十六进制可能有点笨拙,但考虑到5个1/2字节(11个十六进制数字),这可能并非完全不合理。也就是说,如果硬盘驱动器是真正的瓶颈,你可能不会得到很大的速度提升。

因此,为了检查,如果你在2900个fies中搜索,只需读入它们,就可以测量你花了多少时间。甚至不要将它们转换为十六进制。无论搜索算法多么聪明,磁盘I/O所需的时间都是一个下限。如果这还不够好,那就买一个快速SSD。

要获得更快的字符串搜索算法,请查看Boyer-Moore搜索算法。Boost(和c++17)有这样一个实现。

此外,请避免将文件转换为十六进制(std::string可以包含"\0"个字符)。

如果文件IO受到限制,内存映射文件可能是前进的方向。。

虽然这可能是一个存储瓶颈问题,但有一些字符串搜索算法可能比线性算法快得多,例如Boyer-Moore(在https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm),它们确实需要处理搜索模式,并且与线性搜索相比节省了一些内存开销。

基本思想是根据您在给定索引中找到的内容来知道可以跳过多少字符。(i.E.从fileData〔patternLLen-1〕开始,如果字符甚至不在搜索模式中,您可以下一步查看fileData〔patternLen+patternLen-1〕,依此类推

你的模式越长,这样的算法就越有可能比直线搜索有所改进。boost库已经实现了几种这样的改进字符串搜索算法(可以在boost/agorithm\searching/中找到)。