c++oding="utf-8" ?>
应使用循环调用std::string::find和erase删除所有子串,每次找到即删除并重置查找位置,空子串需提前返回以防死循环;regex_replace虽简洁但有性能和转义风险,非重叠删除已满足绝大多数需求。

用 std::string::find + std::string::erase 循环删除最简单
这不是标准库内置操作,得手动实现。核心思路是反复查找子串位置,找到就删掉,直到找不到为止。注意:必须用循环,因为一次 erase 只删第一个匹配,后续可能产生新重叠匹配(比如删 "ab" 时,原串 "abab" 删掉前两个字符后剩下 "ab",还得再删一次)。
常见错误是写成单次查找+删除,结果只删了第一个;或者用 while (s.find(sub) != std::string::npos) 但没更新查找起点,导致无限循环(因为 find 默认从 0 开始,删完前面后,后面内容前移,0 位置又可能匹配)。
- 每次
erase后,下次find应从 0 开始(安全起见),或从删除位置开始(需仔细算偏移) - 推荐始终从 0 查找,逻辑清晰不易错:
size_t pos = s.find(sub); while (pos != std::string::npos) { s.erase(pos, sub.length()); pos = s.find(sub); } - 如果子串为空(
sub.empty()),必须提前返回原串,否则find永远返回 0,陷入死循环
用 std::regex_replace 一行搞定但有隐含开销
如果你不介意正则引擎的启动成本和潜在回溯风险,std::regex_replace(s, std::regex(std::regex_escape(sub)), "") 看起来简洁。但注意:std::regex_escape 不是标准函数(C++11/17 无此函数),得自己实现或用第三方库;否则子串含 .、*、+ 等正则元字符会被误解释。
实际项目中,除非你本来就在用正则、且子串内容可控,否则不推荐。性能上,构造 std::regex 对象开销不小,尤其在高频调用场景下明显慢于手写循环。
- 若坚持用正则,先手工转义:把
"\^$.|?*+()[]{}"中每个字符前加'\' -
std::regex_replace默认替换所有匹配,行为符合需求,但无法控制“是否重叠匹配”(它按非重叠方式处理) - C++11 的
std::regex在部分编译器(如早期 libstdc++)实现不全或极慢,上线前务必实测
需要保留重叠删除?手写状态机更可控
标准循环删除默认是非重叠的:删掉位置 i 的 sub 后,下一次查找从 0 开始,自然能处理新暴露的前缀。但如果你明确要“贪心重叠删除”(例如 s = "aaaa", sub = "aa",想删成空串而非只剩一个 "aa"),那简单循环不够——它每次删完跳过整个 sub.length(),会漏掉重叠部分。
这时得模拟 KMP 或朴素扫描:从左到右逐字符检查,用栈或双指针维护当前待匹配长度。不过绝大多数业务场景不需要这种语义,99% 的“删除所有出现”指非重叠全删,别被算法题误导。
- 重叠删除的典型需求极少,多见于特定编码协议或学术场景
- 若真需要,优先考虑现成字符串匹配库(如
abseil::StrReplaceAll不支持重叠,得另寻方案) - 自己实现时,避免在循环里反复调用
substr,改用string_view做只读切片提升效率
性能敏感时避免频繁内存重分配
每次 erase 都可能触发内部缓冲区收缩,尤其大字符串多次删除小片段时,std::string 可能反复 realloc。如果已知最终结果长度远小于原串,可先预估剩余长度(粗略统计不重叠匹配次数 × sub.length()),用 reserve 预留空间,或改用两段式:先标记所有要删的位置区间,再一次性拷贝保留部分。
但这是典型的过早优化。除非 profiler 明确指出 string::erase 是瓶颈,否则别为这点微优化增加复杂度。现代 libc++ 和 MSVC 的 string 实现对短字符串优化很好,小串删除几乎无感。
- 测试发现:10KB 字符串删 100 次 3 字符子串,循环
erase耗时约 0.02ms,基本可忽略 - 真正卡顿往往来自错误地在循环内做
s += ...拼接,而不是用erase - 如果子串出现频次极高(如每 2 字符就有一个匹配),考虑改用
std::vector<char></char>手动管理内存
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











