std::regex_replace是最直白解法但性能差,适合少量字符串;双指针遍历零依赖、可控、推荐;stringstream方案会清除首尾空格并统一所有空白符为单空格。

用 std::regex_replace 最快写出来(但要注意性能)
如果你只是临时处理少量字符串,且项目已启用 C++11 以上标准,std::regex_replace 是最直白的解法:匹配一个或多个空格,替换成单个空格。
注意它默认会把所有空白符(比如 、
)也当“空格”处理,除非你显式限定为 ASCII 空格 ' ':
std::string s = "a b c
d";
s = std::regex_replace(s, std::regex(" +"), " "); // 只缩 ASCII 空格
// 结果: "a b c
d" —— 和
没被碰,但它们之间的空格被缩了
- 正则
" +"表示“一个及以上连续空格”,比"\s+"更安全,后者会吞掉制表符和换行 - 编译时开
-std=c++11或更高;某些旧版 libstdc++(如 GCC 4.8)的 regex 实现有 bug,慎用 - 性能差:每次调用都构造 regex 对象,对长字符串或高频调用场景不友好
手写双指针遍历(零依赖、可控、推荐)
真正稳定又轻量的方式是原地扫描:用两个索引,一个读(read),一个写(write),跳过连续空格,只保留第一个。
关键点在于“怎么判断‘当前是连续空格中的第一个’”——靠记录上一个字符是不是空格:
立即学习“C++免费学习笔记(深入)”;
std::string compress_spaces(std::string s) {
if (s.empty()) return s;
size_t write = 0;
bool last_was_space = false;
for (size_t read = 0; read < s.length(); ++read) {
if (s[read] == ' ') {
if (!last_was_space) {
s[write++] = ' ';
}
last_was_space = true;
} else {
s[write++] = s[read];
last_was_space = false;
}
}
s.resize(write);
return s;
}
- 只遍历一次,
O(n)时间,O(1)额外空间(原地改) - 严格按字面意思处理:仅针对 ASCII 空格
' ',不会误动、等 - 如果输入是
const std::string&,需先拷贝一份再操作,避免修改原串
用 std::stringstream 拆再拼(语义清晰但有副作用)
利用 std::stringstream 默认以任意空白符(空格、制表、换行)为分隔符的特性,能自动“跳过多余空白”:
std::string s = " a b c
d ";
std::stringstream ss(s);
std::string word, result;
while (ss >> word) {
if (!result.empty()) result += " ";
result += word;
}
// result == "a b c d"
- 结果里所有空白符都被统一成单个空格,且首尾空格也被清除
- 副作用明显:原始格式全丢,
"a b"和"a b"输出一样;无法保留非空格空白符 - 适合“只要词序,不要格式”的场景,比如命令行参数预处理
边界情况和易错点
别小看“缩空格”,实际工程中容易栽在这些地方:
- 输入含 Unicode 空格(如 、
U+3000全角空格)?标准库函数一律不管,得自己用std::iswspace+std::wstring处理 - 想保留开头/结尾空格?双指针方案默认会去掉末尾空格(因为
resize),需额外记录首尾状态 - 多线程环境别直接传引用给
compress_spaces并原地改,除非你确定没其他线程读这个字符串 - 用
regex_replace时,若字符串含正则元字符(如"a.b"),虽然不影响空格替换,但说明你正则意识弱——建议先 review 正则表达式本身是否真需要
最麻烦的其实是需求没说清:到底要“缩 ASCII 空格”、“缩所有空白符”,还是“缩可打印空格但保留不可见控制符”——动手前先确认这点,比选哪个函数重要得多。


















