看起来只是“把字符串里的某个字符删掉”,但 C++ 里这件事并不只有一种写法。真正影响结果的,是你所用的语言标准、字符串长度,以及是否只是删固定字符。下面按常见场景拆开说,顺带把容易踩的坑一并理清:什么时候直接改原字符串最合适,什么时候必须用 erase-remove 惯用法,什么时候又该避开直觉式写法。
C++20:用 std::erase 最省事
如果编译器支持 C++20,那么删除字符串里所有指定字符时,std::erase 基本就是首选。它直接在原字符串上操作,语义很清楚,也省去了手动处理迭代器和边界的麻烦。
典型写法如下:
std::string s = "a#b#c#d";
std::erase(s, '#');
执行后,s 会变成 "abcd"。
这种方式适合“删除固定字符”这一类简单需求,优点主要有三点:
- 原地修改,调用后结果直接落在原字符串中
- 表达直白,代码可读性比手写循环更好
- 整体复杂度是 O(n),通常一次遍历即可完成
很多人一开始会手动写循环配合 erase,看上去更“底层可控”,但实际很容易因为迭代器失效或索引变化写出未定义行为。std::erase 的价值就在于把这些细节收掉了。
需要注意的是,它适用的是 std::string、std::vector 这类标准容器,不适用于 C 风格字符串。
C++11/14/17:正确使用 std::remove_if + erase
如果项目还在 C++11、C++14 或 C++17,最稳妥、兼容性也最好的方案,是经典的 erase-remove 惯用法。这里最关键的一点是:std::remove_if 只是“重排”元素,并不会真的把字符从字符串长度里删掉,所以后面的 erase 不能省。

标准写法如下:
auto it = std::remove_if(s.begin(), s.end(), [](char c) {
return c == '#';
});
s.erase(it, s.end());
这段代码的执行逻辑可以理解为两步:
std::remove_if先把需要保留的字符挪到前面,并返回“新逻辑末尾”位置erase再把这个逻辑末尾之后的那一段真正删掉
因此,只写前半句而不调用 erase,字符串内容看起来可能变了,但长度并没有按预期缩短,这也是初学者最常见的误区之一。
如果要删除多个掩码字符,也只需要改条件即可,例如同时删除 '#' 和 '*':
auto it = std::remove_if(s.begin(), s.end(), [](char c) {
return c == '#' || c == '*';
});
s.erase(it, s.end());
这里还有两个细节值得记住:
- 迭代器范围要用可修改的
s.begin()和s.end(),不要误用s.cbegin() - 对这种删除场景来说,非目标字符的相对顺序本来就会保留下来,不需要额外处理
换句话说,在 C++20 之前,这就是最值得默认采用的写法:兼容范围广,性能和可维护性也都足够可靠。
为什么不推荐 find + erase 循环
不少人第一反应会写成“找到一个,删一个,再继续找下一个”。这种 find 加 erase 的循环方式确实直观,但在字符串稍长、目标字符较多时,问题会很明显。

原因在于:每次 erase 之后,后面的字符都要整体前移;如果随后又继续做一次新的查找,成本会不断叠加,整体很容易退化成 O(n²)。
更麻烦的是,它还容易出现漏删。比如字符串是 "##":
- 第一次删掉位置 0 的
'#' - 原来第二个
'#'会移动到位置 0 - 如果下一次从
pos + 1开始继续找,就可能直接跳过这个字符
这类 bug 不一定每次都能立刻看出来,但一旦输入里有连续掩码字符,就容易暴露。
除非你的字符串非常短,例如少于 10 个字符,而且整个过程只需要删 1 到 2 次,否则这套写法不值得作为常规方案。若确实要坚持使用,至少要满足两点:
- 每次
erase之后,下一次查找起点应当仍设为pos,而不是pos + 1 - 每轮都要判断
pos == std::string::npos,及时结束循环
但从工程角度看,这些手工控制本身就意味着更高的出错率。只要不是演示基础字符串操作,通常都更建议回到前面的两种标准做法。
只有涉及模式匹配时,才考虑 std::regex_replace
正则并不是不能用,而是它解决的是另一类问题。如果你要删除的不是固定单字符,而是某种模式,比如所有空白符 "\s",或者只删除行首的 '#',那 std::regex_replace 才开始有价值。

反过来说,如果目标只是删掉一个固定字符,例如 '*',那写成下面这样通常就是过度设计:
std::regex_replace(s, std::regex("\\*"), "")
这类写法的问题不在于“不能运行”,而在于它为一个简单任务引入了额外复杂度,编译和运行成本都更高。
更适合用正则的情况通常包括:
- 删除规则是动态的,比如从配置中读入一个正则表达式
- 删除条件依赖上下文,比如删掉
//之后整段内容 - 处理的不是单个字符,而是字符类、边界或结构化模式
同时也要留意现实问题:std::regex 在某些标准库实现中,尤其是较旧的 libstdc++ 版本,可能存在性能偏弱甚至实现问题。真要放进生产路径里,最好先测实际吞吐。
如果最终确定要用,通常更适合直接让 std::regex_replace 返回一个新字符串,而不是在原串上做多轮替换,这样更容易控制多次内存分配带来的额外开销。
该怎么选,取决于标准和需求
把字符串中的特定掩码字符删掉,本质上不是难题,难的是选对习惯用法。
- 如果是 C++20,优先用
std::erase(s, '#') - 如果要兼容 C++11/14/17,就用
std::remove_if + erase,两步缺一不可 - 如果只是删固定字符,不要为了“直观”去写
find + erase循环 - 只有当删除规则已经上升到模式匹配层面时,再考虑
std::regex_replace
实际开发里,最容易被忽略的点仍然是 C++20 之前的第二步 erase。漏掉这一步,逻辑上像是删了,结果却没真正删干净。把这一点记牢,基本就能避开这类题目里最常见的坑。













