在输入流中搜索字符串

时间:2016-02-22 17:32:17

标签: c++ iostream

我有一个大的二进制文件(很多千兆字节,因此无法将其加载到内存中)我想搜索所有出现的字符串“icpf”。

我尝试使用std::search来解决这个问题,但是std::search仅适用于前向迭代器,而不是输入迭代器。

标准库是否为此提供了快速替代方案?或者我是否需要手动编码搜索(一次读取数据块然后std::search,或ignore所有内容直到'i',然后手动检查接下来的三个字符)?< / p>

3 个答案:

答案 0 :(得分:1)

最快的方法是将整个文件加载到内存中,然后搜索内存。

下一个最佳选择是让硬盘保持运转状态。也许有一个线程将数据块读入缓冲区,另一个线程则搜索缓冲区。

沿着列表向下,将大块数据读入缓冲区,然后搜索缓冲区是一种很好的技术,尽管效率不如以前的方法。

您可以使用std::getlinestd::string逐行阅读。这不如块读取快,因为输入函数正在搜索换行符(并在std::string中分配内存)。

最糟糕的情况可能是逐字逐句阅读。函数开销对于读取单个字符是不好的(通常,读取大块数据的开销是相同的)。

不,没有用于搜索文件的标准C ++库函数。某些操作系统具有搜索文件的实用程序;也许你可以使用其中之一。

编辑1:
瓶颈是输入数据。一旦你将数据放入缓冲区,那么就是许多有效的搜索算法而不是蛮力(搜索第一个字母,然后搜索下一个字母等)。

在互联网上搜索&#34;字符串搜索算法&#34;。

答案 1 :(得分:1)

  

标准库是否为此提供了快速替代方案?

虽然标准C ++库提供了搜索文本流的方法,但它没有为二进制流提供类似的算法。

  

或者我是否需要手动编码搜索(一次读取数据块然后std::search,或忽略所有内容直到'i',然后手动检查接下来的三个字符)?

编写“跳过和搜索”方法可能很棘手,因为编写跳过条目的解决方案很容易。例如,如果您在包含"icpf"的文件中查找"icpicpf",则一次处理一个字符的简单程序在丢弃"icpf"后将无法找到"icpi"后缀前缀。

如果您要自己编写代码,请考虑实施Knuth–Morris–Pratt algorithm。网上有很多可用的实现,它可以在流上正确运行,因为它一次只考虑一个字符,而且永远不会回溯。

答案 2 :(得分:0)

我不知道任何纯标准库解决方案,但是内核已经实现了预取,因此应该可以mmap()该文件来获取所需的前向迭代器:(省略错误处理)< / p>

size_t search(int fd, size_t fileSize) {
    auto start = reinterpret_cast<char*>(
        ::mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, fd, 0));
    ::madvise(start, fileSize, MADV_SEQUENTIAL);
    auto pattern = "icpf";
    auto offset = std::search(start, start+fileSize, pattern, pattern+4);
    return offset - start;
}

它是一个小小的信念飞跃,相信你的内核可以正确地进行延迟加载,预取和丢弃。另一方面,如果您可以信任任何人,那么可能是内核开发人员。

免责声明:我实际上并没有在一个千兆字节的文件上测试它。