1. 搜索器概述
在 C++17 之前,如果我们想在一个字符串中查找子串,通常使用 std::string::find。虽然它很简单,但有几个明显的局限性:
- 算法单一:C++ 标准库只提供了简单的朴素算法(逐个字符比较)。
- 无法指定算法:如果你想用更高效的算法(如 KMP 或 Boyer-Moore),必须自己实现或寻找第三方库。
- 无法用于容器:
find 是 std::string 的成员函数,不能直接用于 std::vector<char> 或其他容器。
- 接口不统一:字符串查找和泛型算法(如
std::search)的接口不一致。
C++17 引入了搜索器概念。它允许我们把 搜索算法 和 搜索操作 分离开来。现在,标准库里直接提供了Boyer-Moore 和 Boyer-Moore-Horspool 这两个高性能算法!
C++17 在 <functional> 头文件里引入了三种搜索器,每种都基于不同的算法:
std::default_searcher:
- 算法:就是标准库原来用的那种朴素算法。
- 用途:默认选项,短字符串用它足够了。
std::boyer_moore_searcher:
- 算法:Boyer-Moore 算法。工业界最强悍的字符串搜索算法之一。
- 特点:对于短模式不友好,预处理慢,但长文本搜索极快。
std::boyer_moore_horspool_searcher:
- 算法:Boyer-Moore 的简化版(BM-Horspool)。
- 特点:预处理比标准的 BM 快,通常性能也非常好,实际应用中往往比标准 BM 更受欢迎。
这些搜索器是可调用对象(类重载了 operator()),可以传递给 std::search 算法使用。使用时需要包含头文件:
1 2
| #include <functional> #include <algorithm>
|
std::search函数原型:
1 2
| template< class ForwardIt, class Searcher > ForwardIt search(ForwardIt first, ForwardIt last, const Searcher& searcher);
|
- first, last:要搜索的原始字符串的起始位置和结束位置对应的迭代器,区间为左闭右开
- searcher:指定的搜索器
注意:std::search 是 C++17 之前就有的,但在 C++17 中,它增加了一个重载版本,专门接收搜索器对象。
搜索器类构造函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| default_searcher(ForwardIt pat_first, ForwardIt pat_last, BinaryPredicate pred = BinaryPredicate());
boyer_moore_searcher(RandomIt1 pat_first, RandomIt1 pat_last, Hash hf = Hash(), BinaryPredicate pred = BinaryPredicate());
boyer_moore_horspool_searcher(RandomIt1 pat_first, RandomIt1 pat_last, Hash hf = Hash(), BinaryPredicate pred = BinaryPredicate());
|
- pat_first, pat_last:要搜索的子字符串的起始位置和结束位置对应的迭代器,区间为左闭右开
- hf:一个可调用对象,用于对字符串元素进行哈希
- pred:一个可调用对象,用于确定相等性
让我们通过一个具体例子来展示搜索器的使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <iostream> #include <string> #include <vector> #include <functional>
int main() { std::string hay = "This is a long string containing the substring 'algorithm' at the end."; std::string needle = "algorithm";
std::boyer_moore_searcher searcher(needle.begin(), needle.end());
auto it = std::search(hay.begin(), hay.end(), searcher);
if (it != hay.end()) { std::cout << "找到了!位置: " << std::distance(hay.begin(), it) << std::endl; } else { std::cout << "没找到" << std::endl; }
return 0; }
|
2. 实际应用
2.1 大文件搜索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| #include <functional> #include <iostream> #include <string> #include <vector> #include <fstream>
class MultiPatternSearcher { public: MultiPatternSearcher(const std::vector<std::string>& patterns) : m_patterns(patterns) {} std::vector<std::pair<std::string, size_t>> search_in_file(const std::string& filename) { std::vector<std::pair<std::string, size_t>> results; std::ifstream file(filename); if (!file) { throw std::runtime_error("Cannot open file: " + filename); } std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); for (const auto& pattern : m_patterns) { auto searcher = std::boyer_moore_searcher(pattern.begin(), pattern.end()); auto it = content.begin(); while (it != content.end()) { auto result = std::search(it, content.end(), searcher); if (result != content.end()) { size_t pos = std::distance(content.begin(), result); results.emplace_back(pattern, pos); it = result + 1; } else { break; } } } return results; }
private: std::vector<std::string> m_patterns; };
int main() { std::vector<std::string> keywords = {"ERROR", "WARNING", "FATAL", "CRITICAL"}; MultiPatternSearcher searcher(keywords); try { auto results = searcher.search_in_file("logfile.txt"); std::cout << "Found " << results.size() << " occurrences:\n"; for (const auto& [pattern, position] : results) { std::cout << " '" << pattern << "' at position " << position << std::endl; } } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; } return 0; }
|
2.2 自定义数据类型搜索
搜索器不仅可以搜索字符串,还可以搜索任意序列:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| #include <functional> #include <iostream> #include <vector> #include <algorithm>
struct Person { int id; std::string name; int age; bool operator==(const Person& other) const { return id == other.id && name == other.name && age == other.age; } };
int main() { std::vector<Person> database = { {1, "Alice", 30}, {2, "Bob", 25}, {3, "Charlie", 35}, {4, "David", 28}, {5, "Eve", 32} }; std::vector<Person> search_pattern = { {2, "Bob", 25}, {3, "Charlie", 35} }; auto searcher = std::default_searcher(search_pattern.begin(), search_pattern.end()); auto result = std::search(database.begin(), database.end(), searcher); if (result != database.end()) { std::cout << "Pattern found at position: " << std::distance(database.begin(), result) << std::endl; for (auto it = result; it != result + search_pattern.size(); ++it) { std::cout << " ID: " << it->id << ", Name: " << it->name << ", Age: " << it->age << std::endl; } } else { std::cout << "Pattern not found" << std::endl; } return 0; }
|