std::unique不能单独去除所有重复空格,仅能删除相邻重复字符;正确做法是手写一次遍历或C++20用erase_if配合状态机,避免误删非空格重复字符且兼容各类空白符。

直接说结论:std::unique 不能单独去除“所有重复空格”,它只去重相邻重复元素;想把多个连续空格缩成一个,必须先排序或预处理——但空格不需要排序,所以得配合 std::erase_if(C++20)或手写循环 + erase + unique 的组合逻辑。
为什么 std::unique 单独用不行?
std::unique 只扫描相邻相等元素,把重复的“挤到末尾”,再返回新逻辑终点迭代器。它不识别“空格是否连续”,只看当前和前一个字符是否相同。
比如字符串 "a b"(中间三个空格),std::unique 会保留第一个空格、删掉第二个(因与前一字符相同),但第三个空格和第二个“不相邻”于新序列中——等等,其实它是一次遍历:原始序列中索引1和2都是空格,会被视为连续重复,所以最终结果是 "a b"(两个空格变一个),看似可行?错:它只对“连续相同字符”生效,但前提是调用方式正确;常见错误是忘了接 erase。
- 错误写法:
std::unique(s.begin(), s.end())—— 这只是移动了迭代器,s长度没变,末尾残留脏数据 - 正确配对:
s.erase(std::unique(s.begin(), s.end()), s.end())—— 但这仍只是去所有相邻重复字符,不是“仅针对空格” - 副作用:把
"aa bb"变成"a b",字母也被去重了
真正安全的“多空格→单空格”做法(C++20)
用 std::erase_if 配合状态机逻辑:记录上一个字符是否为空格,跳过后续连续空格。
立即学习“C++免费学习笔记(深入)”;
更推荐的是手写一次遍历——清晰、无依赖、性能好:
std::string compress_spaces(const std::string& s) {
std::string out;
out.reserve(s.size()); // 避免反复扩容
bool last_was_space = false;
for (char c : s) {
if (c == ' ') {
if (!last_was_space) {
out += ' ';
}
last_was_space = true;
} else {
out += c;
last_was_space = false;
}
}
return out;
}
- 不修改原串,线性时间,只遍历一遍
- 不会误删非空格重复字符(如
"aa"保留) - 兼容任意编码(不依赖 locale 或宽字符特殊处理)
如果坚持用 std::unique,怎么限定只处理空格?
可以自定义谓词,但注意:std::unique 的谓词是二元的,用于比较“相邻两元素是否应合并”,不能直接判断“是否为空格”。所以得先预处理:把所有非空格字符替换为占位符,再 unique,再还原——太绕,不实用。
真正能结合 unique 的技巧,是先用 std::adjacent_find 找出所有连续空格起点,再分段 erase——反而比手写循环慢且难维护。
- 别为了用
std::unique而用它;它适合去重有序序列(如sort + unique),不是为空格压缩设计的 - 若项目强制 C++17 或更早,没有
erase_if,就用上面的手写循环,别硬套算法库 -
std::unique的返回迭代器指向“新逻辑尾”,必须用erase显式截断,否则size()不变、data()末尾不可靠
最易被忽略的一点:空格不只是 ASCII ' ',实际场景可能含 '\t'、'\n'、Unicode 空白字符。如果需求是“所有空白字符去重”,就得用 std::isspace(注意 locale)替代 c == ' ' 判断——这时候手写循环依然最可控。


















