在输入流中搜索字符串

Searching for a string in an input stream

本文关键字:字符串 搜索 输入流      更新时间:2023-10-16

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

我尝试为此使用 std::search,但只是因为 std::search 仅适用于前向迭代器,而不适用于输入迭代器这一事实而感到困惑。

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

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

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

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

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

后缀。

如果你打算自己编写代码,请考虑实现Knuth-Morris-Pratt算法。网上有许多实现,它在流上可以正常运行,因为它一次考虑一个字符,并且永远不会返回。

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

下一个最佳选择是保持硬盘驱动器运动。 也许有一个线程将数据块读取到缓冲区中,另一个线程搜索缓冲区。

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

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

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

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

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

在互联网上搜索"字符串搜索算法"。

我不知道

有任何纯标准库解决方案,但内核已经实现了预取,所以应该可以mmap()文件以获得所需的转发迭代器:(错误处理省略)

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;
}

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

免责声明:我实际上并没有在数 GB 的文件中对此进行测试。