最直接适合敏感词数量少:用 std::string::find + std::string::erase 逐个替换。

用 std::string::find + std::string::erase 逐个替换最直接
适合敏感词数量少(erase 后要重置搜索起始位置,否则会跳过紧邻的词。
常见错误:在循环中用固定 pos = 0 开始找,或擦除后没更新 pos,导致重复词只删第一个。正确做法是让 pos 从上次擦除后的位置继续:
std::string text = "这个App很黄很暴力";
std::vector<:string> badwords = {"黄", "暴力"};
for (const auto& w : badwords) {
size_t pos = 0;
while ((pos = text.find(w, pos)) != std::string::npos) {
text.erase(pos, w.length());
// 不加 pos += 0,因为擦除后后续内容前移,下次从同一位置再查(防"aa"删成"a"后漏掉下一个"a")
}
}</:string>
⚠️ 注意:find 是子串匹配,不区分词边界——“发票”会误杀“开发票”里的“发票”。如需整词过滤,得加空格/标点判断,或改用正则。
用 std::regex_replace 实现带边界控制的过滤
当需要“仅匹配独立词汇”(如“枪”不匹配“手枪”,但匹配“一把枪”中的“枪”),正则的单词边界 \b 是最简方案。但 C++11 的 std::regex 在 GCC 低版本有性能 bug,MSVC 支持较好。
实操要点:
- 构造正则时对每个敏感词做
std::regex_escape(C++20 起才有;C++17 及以前需手动转义.、*、?等) - 用
(?:word1|word2|word3)合并所有词,避免多次扫描;加上\b包裹实现整词匹配 - 替换为空字符串即可
示例(C++20):
std::string text = "别提枪和手枪,只说枪支";
std::vector<:string> badwords = {"枪", "枪支"};
std::string pattern = "\b(?:";
for (size_t i = 0; i 0) pattern += "|";
pattern += std::regex_replace(badwords[i], std::regex(R"([\^$.|?*+(){}])"), R"($&)");
}
pattern += ")\b";
text = std::regex_replace(text, std::regex(pattern), "");</:string>
高频/长文本场景必须用 AC 自动机(Aho-Corasick)
当敏感词库达几百上千个、日均处理 GB 级文本(如聊天消息实时过滤),find 或正则会 O(n×m) 暴力扫描,CPU 直接拉满。AC 自动机把多模式匹配降到 O(n + m),是工业级选择。
关键取舍点:
- 不建议手写——容易出错且难调试;推荐用成熟轻量库,如
ahocorasick(header-only,#include "ahocorasick.hpp") - 构建自动机是一次性开销,之后每次匹配可复用;务必缓存
AcTrie实例,别每次新建 - 它默认返回所有匹配位置,你要自己遍历并标记/擦除;注意重叠匹配(如"ab"和"abc")需按长度或优先级合并处理
简单示意(伪代码):
AcTrie trie;
for (const auto& w : badwords) trie.add(w);
auto matches = trie.search(text); // vector of {start, end, word}
// 逆序遍历 matches,防止 erase 后索引失效
std::sort(matches.rbegin(), matches.rend());
for (const auto& m : matches) {
text.erase(m.start, m.end - m.start);
}
大小写、全半角、形近字等变体怎么处理
纯字符串匹配对 “shui”(全角)、"Shui"、"5hui"(数字 5 替代 s)完全无效。这不是算法问题,而是预处理问题。
通用做法分两层:
- 输入归一化:进过滤器前,统一转小写、全角转半角(用 Unicode 映射表)、常见形近字映射(如 '0'→'0', 'Ⅰ'→'I')
- 词库扩展:对每个原始敏感词,生成常见变体加入自动机(如 "fuck" 补 "f**k", "f@ck"),但别无脑爆破——控制变体总数,否则内存暴涨
⚠️ 最容易被忽略的是:归一化函数本身必须高效,不能在每条消息里反复调用 std::wstring_convert 或正则。建议用查表法(std::array<uint8_t></uint8_t> 做 ASCII 映射 + 小范围 Unicode 特殊处理)。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











