“字符串减法”指从原字符串中移除所有与指定子串完全匹配的连续片段,非字符集合差或数值相减;标准做法是用find+erase循环实现非重叠贪心删除,如"ab123cd123ef123"减"123"得"abcdef"。

什么是“字符串减法”?先明确操作边界
C++ 没有内置的 std::string - std::string 运算符,所谓“字符串减法”实际是从原字符串中移除所有与某个子串完全匹配的连续片段,不是按字符集合差集处理,也不是数值相减。常见误解是以为像 Python 的 set(str1) - set(str2),但这里要的是子串级别的、有序的、非重叠(或可重叠)删除。
关键判断点:是否允许重叠匹配?比如在 "aaaa" 中删掉 "aa",结果是 "aa"(非重叠)还是空(重叠匹配两次)?标准做法默认非重叠、贪心从前向后匹配,这也是 std::string::find 和多数实现的基础。
用 std::string::find + erase 循环最稳妥
这是最直观、可控性最强的方式,适合大多数场景(中小长度字符串、不追求极致性能):
- 每次调用
find定位首个匹配位置 - 用
erase删除该处子串 - 重复直到
find返回std::string::npos
std::string s = "ab123cd123ef123";
std::string to_remove = "123";
size_t pos = 0;
while ((pos = s.find(to_remove, pos)) != std::string::npos) {
s.erase(pos, to_remove.length());
// 注意:此处不加 pos += to_remove.length(),因为 erase 后后续内容已前移
}
// 结果: "abcdef"
- 如果需要重叠匹配(如
"111"删"11"得"1"),则每次pos++而非跳过整个匹配长度 -
erase是原地修改,多次调用可能触发多次内存搬移,对超长字符串(MB 级)要注意性能 - 不要用
for (auto it = s.begin(); ...)边遍历边删,迭代器会失效
用 std::regex_replace 简洁但有隐含成本
适用于模式较灵活(比如想删所有数字串、或带边界限制),但纯字面量删除时有点杀鸡用牛刀:
立即学习“C++免费学习笔记(深入)”;
#include <regex>
std::string s = "ab123cd123ef123";
s = std::regex_replace(s, std::regex("123"), "");
- 默认行为是非重叠匹配,和
find方式一致 - 正则引擎启动开销大,短字符串下比循环
find慢 2–5 倍 -
std::regex在部分旧标准库(如 libstdc++ < 4.9)中存在 bug 或不完整,跨平台需验证 - 若
to_remove含正则元字符(如"."、"*"),必须转义,否则行为意外
避免 replace 或 substr 手动拼接的坑
有人试图用 substr(0, pos) + substr(pos+len) 拼接,看似函数式,实则隐患多:
- 每次
substr都分配新字符串,N 次删除 → O(N²) 时间 + O(N²) 内存 -
pos计算易出错:漏判npos、越界访问substr(pos+len)导致std::out_of_range -
replace(pos, len, "")看似等价,但底层仍调用erase,无额外优势,反而多一层封装
更安全的做法是坚持单一 erase 循环,或预分配结果空间用两指针写入:
- 先统计匹配次数和总删除长度
- 构造目标容量的
std::string result - 用两个索引(读位置、写位置)单趟扫描填充,避免反复内存操作
复杂点在于:匹配逻辑一旦变复杂(如忽略大小写、按 Unicode 字符边界删),就很难靠基础 find 覆盖,得切到 std::regex 或 ICU 库——但那已经超出“移除所有匹配数值”的原始需求了。


















