最直接方案是用std::regex_replace匹配s+替换为单空格再手动trim首尾,但性能差、不支持Unicode且不能原地修改;生产推荐双指针法,O(n)时间O(1)空间原地去重并trim。

用 std::regex_replace 一行删掉所有连续空格(含首尾)
最直接的方案是正则替换,把一个或多个空白字符(s+)全换成单个空格,再 trim 首尾。但注意:std::regex 在 MSVC 和 libstdc++ 上性能差、部分版本不支持 Unicode 空白(如 ),且不能原地修改。
实操建议:
- 仅用于小字符串、低频调用场景;高频或大文本慎用
- 用
R"(s+)"而非"\s+"避免转义错误 - 替换后需手动去掉首尾空格,
std::regex_replace不自动 trim
std::string s = " hello world
";
s = std::regex_replace(s, std::regex(R"(s+)"), " ");
// → " hello world ",还要再 trim
s.erase(0, s.find_first_not_of(" "));
s.erase(s.find_last_not_of(" ") + 1);
// → "hello world"
手写双指针遍历,真正高效且可控
这是生产环境推荐做法:O(n) 时间、O(1) 额外空间(原地修改),完全避开正则开销和兼容性问题。核心逻辑是「读写分离」——用一个写指针 write 记录有效字符应放位置,读指针 read 扫描整个字符串。
关键细节:
立即学习“C++免费学习笔记(深入)”;
- 跳过开头所有空格,第一个非空格字符必须写入
write = 0 - 遇到非空格字符,直接写入并推进
write - 遇到空格时,只在前一个字符不是空格的前提下才写入一个空格(即去重)
- 最后用
s.resize(write)截断多余部分
void removeExtraSpaces(std::string& s) {
int write = 0;
bool prevWasSpace = true; // 初始设为 true,确保首字符前不写空格
for (int read = 0; read < s.length(); ++read) {
if (std::isspace(static_cast<unsigned char>(s[read]))) {
if (!prevWasSpace) {
s[write++] = ' ';
prevWasSpace = true;
}
} else {
s[write++] = s[read];
prevWasSpace = false;
}
}
if (write > 0 && s[write-1] == ' ') --write; // 去掉末尾可能多写的空格
s.resize(write);
}
用 std::unique + erase 只删连续重复空格(不处理首尾)
这个方法常被误认为能“一键去多余空格”,但它只对连续重复字符起作用,且默认只比较相邻元素是否相等,无法识别“空格 vs 制表符”这类不同空白字符。它不会动首尾空格,也不压缩多种空白为一种。
适用场景有限:
- 输入已保证只有空格(无
、),且你只要压缩连续空格,保留首尾 - 配合自定义谓词可扩展,但会失去简洁性
- 注意
std::unique不真正删除,只是挪到末尾,必须接erase
std::string s = "a b c"; // 含制表符,std::unique 默认无效
s.erase(std::unique(s.begin(), s.end(),
[](char a, char b) { return std::isspace(a) && std::isspace(b); }),
s.end());
// 结果不确定,因谓词未覆盖所有空白组合,且未处理首尾
为什么不用 std::stringstream 拆分再拼接?
常见误区是用 std::stringstream 读单词再用空格连接。它确实能去掉所有多余空白,但代价明显:
- 分配新字符串内存,原字符串被抛弃,无法原地修改
- 每个单词都要拷贝一次,中间还涉及流解析开销
- 丢失原始空白语义(比如想保留换行符只删空格,它就全抹了)
- 遇到空字符串或全是空白时,结果为空,可能不符合预期
除非你明确需要「按空白切分后重新规整为单空格分隔」,否则没必要绕这一圈。
真正难的不是选哪个函数,而是想清楚需求:要不要保留非空格空白(如换行)?是否允许额外内存分配?是否必须原地修改?这些决定了该用双指针还是正则,而不是看哪段代码更短。


















