“字符串减法”指从字符串s1中移除所有s2子串的字面匹配,生成新字符串,需手动实现;标准库无-运算符,常用find+substr或regex_replace(后者需转义元字符),注意避免原串修改与重叠匹配问题。

什么是“字符串减法”?先明确操作语义
C++ 标准库没有 std::string 的 - 运算符重载,所谓“字符串减法”通常指从一个字符串中移除另一个字符串的所有出现(或首次出现),生成新字符串,且不修改原对象。这不是数学减法,而是“过滤式删除”。
常见误解是试图写 s1 - s2 直接编译通过——这会报错:invalid operands to binary expression ('std::string' and 'std::string')。必须自己实现逻辑。
用 std::string::find + std::string::substr 手动构建结果
这是最可控、兼容性最好(C++11 起)、不依赖第三方的方法。核心思路:遍历查找 s2 在 s1 中的位置,跳过匹配段,拼接剩余部分。
- 每次调用
find得到起始位置pos,若为std::string::npos则结束 - 用
substr(0, pos)取前段,再从pos + s2.length()开始继续查找 - 推荐用
std::string result累加,避免反复拷贝大字符串(可预留容量:result.reserve(s1.length()))
std::string string_subtract(const std::string& s1, const std::string& s2) {
if (s2.empty()) return s1;
std::string result;
result.reserve(s1.length());
size_t pos = 0;
while (true) {
size_t found = s1.find(s2, pos);
if (found == std::string::npos) {
result += s1.substr(pos);
break;
}
result += s1.substr(pos, found - pos);
pos = found + s2.length();
}
return result;
}
注意:此版本默认删除所有匹配子串。若只需删首次出现,找到后直接 break 即可。
用 std::regex_replace 实现更灵活的“减法”
如果需求是“删掉所有 s2 字面量”,std::regex_replace 更简洁;但要注意:
-
s2中含正则元字符(如.、*、^)会被解释为模式,不是字面匹配 - 若想安全做字面替换,需先转义:
std::regex_replace(s1, std::regex(std::regex_replace(s2, std::regex(R"([.*+\?\^\$|{}[]()\])"), R"(\$&)")), "")—— 太重,一般不推荐 - 性能比手写
find低,尤其短字符串场景
所以除非你本来就要正则能力(比如删掉“a\d+b”这种模式),否则别为简单字面删除引入 std::regex。
别踩坑:erase 会改原对象,replace 默认也非只读
-
s1.erase(...) 直接修改 s1,违反“不改变原对象”前提
-
std::string::replace 同样就地修改,即使你传入副本,也要确保操作的是副本而非引用
- 传参务必用
const std::string&,返回新对象;切忌在函数内对参数调用任何非 const 成员函数
- 如果误写成
auto s = s1; s.erase(...); return s;,虽然结果正确,但多一次拷贝,不如直接构造 result 高效
s1.erase(...) 直接修改 s1,违反“不改变原对象”前提std::string::replace 同样就地修改,即使你传入副本,也要确保操作的是副本而非引用const std::string&,返回新对象;切忌在函数内对参数调用任何非 const 成员函数auto s = s1; s.erase(...); return s;,虽然结果正确,但多一次拷贝,不如直接构造 result 高效真正容易被忽略的点是:很多人写完发现删不干净,其实是没处理重叠匹配(如 s1="aaaa", s2="aa",删完剩 "aa" 还是含 "aa")。上面 find 版本默认不重叠(从 pos + len 继续找),符合常规预期;如需重叠删除(即贪心删),得把 pos 改为 found + 1,但那是另一类需求了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











