用std::unordered_set<char>做白名单过滤最直接,但需注意char符号性、UTF-8多字节破坏及regex性能问题;ASCII场景可用,UTF-8须按码点解码后过滤。

用 std::string 和 std::unordered_set 做白名单过滤最直接
白名单过滤本质是“只保留指定字符”,核心逻辑就是遍历原字符串,对每个字符查表判断是否允许保留。用 std::unordered_set<char> 存白名单字符,平均 O(1) 查找,比每次遍历 vector 或 string 快得多。
常见错误是把白名单写成 std::string 然后用 find() 查,尤其在长字符串+高频调用时性能明显下降;还有人忽略 char 的符号性问题(比如某些平台 char 默认 signed,遇到 0x80–0xFF 字节会转成负值,导致查表失败)。
- 白名单初始化建议用 initializer list:
std::unordered_set<char> whitelist{'a', 'b', 'c', '0', '1', '-', '_'}</char> - 如果要支持 ASCII 扩展字符(如带重音的拉丁字母),别用
char,改用unsigned char或uint8_t,并确保 set 里也存同类型 - 过滤时用
result += c比result.push_back(c)稍慢(触发多次 realloc),但可读性好;若性能敏感,先reserve()预估长度
黑名单过滤别直接 erase() 迭代器,容易越界
黑名单逻辑是“删掉禁止字符”,但边遍历边删 std::string 容易让迭代器失效或跳过下一个字符。典型错误写法:for (auto it = s.begin(); it != s.end(); ++it) { if (blacklist.count(*it)) s.erase(it); } —— erase() 返回新迭代器,不接住就崩。
更安全的做法是两步走:先标记要删的位置,再批量删除;或者用 std::remove_if + erase() 组合(erase-remove idiom)。
立即学习“C++免费学习笔记(深入)”;
- 推荐写法:
s.erase(std::remove_if(s.begin(), s.end(), [&blacklist](char c) { return blacklist.count(c); }), s.end()); - 注意
std::remove_if不真删,只是把保留元素前移,返回新逻辑结尾,必须配erase() - 如果黑名单含控制字符(如
' '、' '),确认它们是否真该被过滤——有时空格和换行需要特殊处理
处理 UTF-8 字符串时,不能按 char 单字节过滤
UTF-8 是变长编码,一个 Unicode 字符可能占 1–4 字节。std::string 存的是字节流,直接对每个 char 做白/黑名单,会把多字节字符拆开,导致乱码或崩溃(比如只删掉某个汉字的前两个字节)。
除非明确只处理纯 ASCII 输入,否则必须按 Unicode 码点(code point)切分。C++20 <charconv> 和 <locale> 支持有限,实际项目中更常用轻量库如 utf8cpp 或手动解析:
- 先用
utf8::iterator(utf8cpp)或自定义函数把std::string解析成std::vector<uint32_t>码点序列 - 对每个码点做白/黑名单判断(此时查表用
std::unordered_set<uint32_t>) - 再把通过的码点重新编码回 UTF-8 字节流
- 忽略 BOM、代理对(surrogate pairs)等边界情况,会导致过滤后字符串损坏
std::regex 能做但不推荐用于简单过滤
有人用 std::regex_replace 写白名单:std::regex_replace(s, std::regex("[^abc0-9_-]"), ""),语法简洁,但开销大:每次调用都编译正则(即使复用 std::regex 对象也有状态管理成本),且错误处理弱(std::regex_error 容易被忽略)。
真正适合 regex 的场景是模式复杂、动态变化(比如从配置读规则),而不是固定字符集。另外 Windows 下 MSVC 的 std::regex 实现长期有 bug,GCC/Clang 也未必完全兼容。
- 如果坚持用 regex,务必提前构造
const std::regex对象,避免循环内重复构造 - 白名单 regex 中
[^...]表示“非这些字符”,注意^在方括号内才取反,放外面是行首锚点 - 测试时用含
'\'、'['、']'的输入,确认转义是否正确——比如过滤反斜杠得写"\\\"
字符集过滤看似简单,但混入编码、符号类型、迭代器安全、性能预期之后,每一步都容易偏离预期。尤其是把 ASCII 逻辑直接套到 UTF-8 上,几乎必然出错。


















