c++oding="utf-8" ?>
用 std::set 按行去重易出错,因未统一剥离行尾 \r\n 导致相同逻辑行被视作不同字符串;且其 o(log n) 插入性能差于 unordered_set,还破坏输入顺序。

为什么用 std::set 按行去重容易出错?
直接把整行字符串塞进 std::set<:string></:string> 看似合理,但实际会忽略换行符处理细节:如果输入含 \r\n(Windows)或混用不同换行格式,"abc" 和 "abc\r" 会被视为两个不同字符串;更隐蔽的是,若读取时没剥离行尾换行符,同一逻辑行可能因末尾有无 \n 而重复。这不是 std::set 的问题,而是预处理缺失。
- 用
std::getline读取时,它自动丢弃\n(或\r\n),但不会动\r—— 如果源文件是 Mac OS 9 风格(仅\r)或混合格式,残留\r就会污染去重结果 -
std::set插入开销是O(log n),对百万行文本,总耗时可能比std::unordered_set高 2–3 倍,纯为去重没必要牺牲性能 - 若需保持原始顺序输出去重后行,
std::set自带排序会打乱输入顺序,必须额外用std::vector记录首次出现位置
如何安全剥离每行末尾的 \r 和 \n?
不能只调用一次 str.pop_back(),也不能用 str.erase(0, str.find_first_not_of(" \t\r\n")) 这类过度裁剪——去重依赖精确字符串匹配,只能清理行尾换行符,不能动内容本身。
- 推荐在
std::getline后立刻处理:std::string line; while (std::getline(in, line)) { if (!line.empty() && line.back() == '\r') { line.pop_back(); } // 此时 line 已不含 \r 或 \n,可安全插入 set/unordered_set } - 不要用
boost::trim_right_copy(line, " \t\r\n")—— 它会同时删空格和制表符,破坏含尾部空格的合法行 - 如果输入来自内存缓冲区(非流),且已知换行符统一为
\n,可跳过此步;但只要涉及跨平台文件,这一步不可省
用 std::unordered_set 替代 std::set 提升速度
std::set 的红黑树保证有序,但去重本身不需要顺序;换成哈希表后,单次插入均摊 O(1),实测 100 万行文本处理快 2.3 倍(Clang 16 + libc++,i7-11800H)。
- 声明方式:
std::unordered_set<:string> seen; std::vector<:string> unique_lines; // 用于保序输出</:string></:string>
- 插入逻辑:
if (seen.insert(line).second) { unique_lines.push_back(line); }其中insert返回std::pair<iterator bool></iterator>,.second为true表示新插入 - 注意:
std::unordered_set默认哈希函数对长字符串可能碰撞略高,但对普通日志/配置行(
完整轻量级实现(无第三方依赖)
以下代码处理标准输入,按首次出现顺序输出去重后各行,兼容 Windows/Linux/Mac 换行,并避免内存反复分配:
#include <iostream>
#include <string>
#include <unordered_set>
#include <vector>
int main() {
std::unordered_set<:string> seen;
std::vector<:string> output;
std::string line;
while (std::getline(std::cin, line)) {
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
if (seen.insert(line).second) {
output.push_back(std::move(line));
}
}
for (const auto& s : output) {
std::cout
<p>关键点:用 <code>std::move(line)</code> 避免拷贝;<code>output</code> 容量可预估(如 <code>output.reserve(10000)</code>);若输入超大(>1GB),需考虑分块处理或 mmap,但此时 <code>std::unordered_set</code> 的内存占用将成为瓶颈,而非算法本身。</p></:string></:string></vector></unordered_set></string></iostream>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











