预建std::unordered_set黑名单比每次遍历更高效,因其平均查找复杂度为O(1),而遍历数组或字符串为O(n×m);应避免std::set(O(log m))和边遍历边erase(导致迭代器失效与性能跳变),改用双指针原地覆盖。

用 std::unordered_set 预建黑名单比每次遍历更高效
直接在清洗循环里对每个字符查黑名单数组或字符串,时间复杂度是 O(n×m),当黑名单有几十个字符、待清洗字符串上万时明显拖慢。正确做法是把黑名单一次性载入 std::unordered_set<char></char>,后续每次判断都是平均 O(1)。
- 构造时用初始化列表:
std::unordered_set<char> blacklist{'', '&', '"', '\''};</char> - 若黑名单来自配置文件或运行时输入,用
insert()批量加载,避免单次insert的哈希重散列开销 - 注意:不要用
std::set<char></char>——红黑树查找是 O(log m),无必要且更慢
清洗逻辑别用 erase() 在原字符串上边遍历边删
std::string::erase() 会引发内存搬移,边遍历边删导致迭代器失效、性能跳变,还容易漏字符。应该用双指针(写指针 + 读指针)原地覆盖,最后一次 resize()。
- 示例片段:
size_t write_pos = 0;<br>for (size_t read_pos = 0; read_pos < s.length(); ++read_pos) {<br> if (blacklist.find(s[read_pos]) == blacklist.end()) {<br> s[write_pos++] = s[read_pos];<br> }<br>}<br>s.resize(write_pos); - 若需保留原始字符串,改用
std::string result;+reserve()预分配空间,避免多次扩容 - 别用
std::remove_if+erase组合——它本质也是移动,且额外构造 predicate 对象,对简单字符判断属于过度设计
处理 Unicode 字符时 char 黑名单会失效
如果字符串含 UTF-8 编码的中文、emoji,单个 char 无法表示完整字符,黑名单查的是字节而非字符,会导致误删或漏删。此时必须先做 UTF-8 解码,再按 Unicode 码点判断。
- 轻量方案:用
utf8cpp库的utf8::next()逐码点解析,把黑名单存为std::unordered_set<uint32_t></uint32_t> - 不建议手写 UTF-8 解码——边界情况(如非法序列、代理对)极易出错
- 若确定输入全是 ASCII,可跳过此步;但只要可能混入非 ASCII,就必须区分“字节黑名单”和“字符黑名单”语义
编译器优化下,std::string_view 可省掉一次拷贝
如果清洗函数只是临时过滤、不修改原始数据,传入 std::string_view 比 const std::string& 更轻量,尤其对长字符串或频繁调用场景。
立即学习“C++免费学习笔记(深入)”;
- 函数签名应为:
std::string sanitize(std::string_view input, const std::unordered_set<char>& blacklist)</char> - 返回新
std::string,内部仍用双指针填充,避免隐式转换开销 - 注意:不能返回
std::string_view指向局部变量,否则悬垂


















