“字符串减法”实为行级稳定去重:按换行符分割输入字符串,用unordered_set记录已见行,仅保留首次出现的行并维持原始顺序,需统一处理\r\n/\n、安全清理\r、避免trim破坏格式,语义重复判定须由用户明确定义。

什么是“字符串减法”:实际是行级去重而非数学运算
在 C++ 中没有原生的 string - string 运算符支持“删除重复行”,所谓“字符串减法”其实是对多行文本按行切分后,保留首次出现的行、跳过后续重复行——本质是行去重(stable deduplication),不是字符级差集。误以为能像 Python 的 set 那样直接相减,会导致逻辑错误或编译失败。
- 输入通常是含换行符的
std::string,比如从文件读入或网络接收的文本 - 必须按
'\n'(或"\r\n")分割,不能用std::string::find直接删子串,否则会破坏非重复行的内部结构 - Windows 行尾
"\r\n"和 Unix"\n"要统一处理,否则"abc\r\n"和"abc\n"会被视为不同行
用 std::istringstream + std::unordered_set 实现稳定去重
这是最常用且可靠的方式:逐行提取、用哈希表记录已见行、只输出未见过的行。关键在于保持原始顺序,且不修改每行内容(包括空行和空白符)。
- 用
std::getline(is, line)每次读取一行,自动剥离'\n'(但保留'\r',需手动清理) - 对每行调用
line.erase(line.find_last_not_of(" \t\r\n") + 1)前先判断是否为空,避免string::npos + 1溢出;更安全的做法是先去掉末尾'\r':if (!line.empty() && line.back() == '\r') line.pop_back(); -
std::unordered_set<:string></:string>查重是 O(1) 平均复杂度,但注意:如果某行超长(如几 MB),哈希计算本身会变慢,此时应考虑加长度检查或改用std::set(O(log n) 但内存更稳)
std::string dedupe_lines(const std::string& input) {
std::istringstream iss(input);
std::string line;
std::unordered_set<std::string> seen;
std::string result;
<pre class='brush:php;toolbar:false;'>while (std::getline(iss, line)) {
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
if (seen.insert(line).second) { // insert 返回 pair<iter, bool>,second 为 true 表示新插入
if (!result.empty()) result += '\n';
result += line;
}
}
return result;}
遇到空行和全空白行该怎么处理?
默认行为是把空行 "" 和仅含空格的行(如 " \t ")当作不同字符串处理。如果你希望“忽略空白差异”,必须显式标准化:
立即学习“C++免费学习笔记(深入)”;
- 若需视
" \n"和"\n"为同一行,应在插入前做 trim:auto trimmed = trim(line);,其中trim函数需自己实现(用find_first_not_of+find_last_not_of) - 但要注意:trim 后可能把有效空白行变成
"",而原始文本中多个连续空行会被压缩成一个,这属于需求取舍,不是 bug - 如果原始格式敏感(如配置文件、代码片段),就不要 trim,直接用原行比较
性能瓶颈常出现在大文件和重复率高的场景
当输入超过 10MB 或单行重复上万次时,std::unordered_set 可能因哈希冲突变慢,甚至触发 rehash 导致内存抖动。
- 预先用
seen.reserve(expected_unique_count)减少 rehash 次数;若无法预估,设为input.length() / average_line_length的 1.5 倍较稳妥 - 若重复行高度集中(如日志里连续千行相同),可先用
std::unique配合std::vector做相邻去重(更快但不适用于乱序重复) - 极端情况(GB 级文本)应改用流式处理 + 外部排序或布隆过滤器(
boost::bloom_filter),但 C++ 标准库不提供,需引入第三方
真正难的不是写对逻辑,而是判断哪一行该被“认为重复”——是字面相等?忽略首尾空格?忽略大小写?还是按正则归一化?这些语义必须由你明确定义,库不会替你决定。


















