std::unique配合erase是最直接的连续重复字符去重方法,但需字符串已排序或仅处理相邻重复;它返回新逻辑结尾迭代器,必须erase才真正删除,且不适用于string_view。

用 std::unique 配合 erase 是最直接的方法
前提是字符串已排序或你只关心“连续重复”——std::unique 本身只移除相邻重复元素,不改变顺序,也不保证全局去重。它返回一个迭代器指向新逻辑结尾,必须配合 erase 才真正删除内存中的冗余字符。
常见错误是只调用 std::unique 却忘了 erase,结果字符串长度没变,只是后面内容被覆盖,打印时可能看到乱码或残留旧字符。
-
std::unique不适用于std::string_view(它不可修改),只能用于可变的std::string - 注意包含头文件:
#include <algorithm>和#include <string> - 示例:
std::string s = "aaabbbcccd";<br>s.erase(std::unique(s.begin(), s.end()), s.end()); // 结果为 "abcd"
手动遍历更可控,适合带条件的去重
比如你想保留最多 2 个连续相同字符(即 “aaab” → “aabb”),或者跳过某些字符(如空格、标点)再判断重复,这时候手写循环反而更清晰,不容易出错。
核心思路是用两个指针:一个读位置 read,一个写位置 write;只在 s[read] != s[write-1] 时才写入,避免边界越界。
立即学习“C++免费学习笔记(深入)”;
- 初始时
write = 1(首字符默认保留),read从 1 开始遍历 - 必须检查
write > 0才能取s[write-1],否则对空串崩溃 - 最后调用
s.resize(write)或s.erase(write) - 示例(纯连续去重):
if (s.empty()) return;<br>size_t write = 1;<br>for (size_t read = 1; read < s.size(); ++read) {<br> if (s[read] != s[write-1]) {<br> s[write++] = s[read];<br> }<br>}<br>s.resize(write);
正则表达式可行但不推荐用于简单场景
用 std::regex_replace(s, std::regex("(.)\1+"), "$1") 确实一行搞定,但性能差、编译开销大、跨平台兼容性弱(MSVC 的 std::regex 实现长期有 bug,GCC/Clang 表现也不稳定)。
除非你已经在项目中重度依赖正则,且处理的是混合模式(比如“删连续重复字母,但保留连续数字”),否则没必要引入复杂度。
- 需要
#include <regex>,C++11 起支持,但 C++17 前很多标准库实现不完整 - 错误信息如
std::regex_error: regex_error(error_stack)很常见,尤其在嵌套量大的模式下 - 对短字符串(
注意 const char* 和 string_view 的陷阱
如果你拿到的是 const char* 或 std::string_view,不能直接改——必须先拷贝到 std::string 再处理。有人误以为 string_view 可写,结果编译不过或运行时段错误。
-
std::string_view构造时不分配内存,底层指针可能指向只读区(如字面量) - 安全做法:
std::string s(data.data(), data.size());<br>s.erase(std::unique(s.begin(), s.end()), s.end());
- 如果原始数据很大且只读,又不想拷贝,那就只能用只读算法(如输出到新 buffer),无法原地删
实际用的时候,90% 场景选第一种就够了。真正容易被忽略的是:连续重复的定义是否包含大小写("AaA" 算不算重复)、是否要保留 Unicode 多字节字符边界(比如 UTF-8 下一个汉字占 3 字节,不能按 char 拆)。这些得根据输入数据源提前确认。


















