std::set直接存std::string不能解决“按行去重”,因为其默认将带换行符(如"\n"、"\r\n")与不带换行符的相同内容视为不同字符串;必须先对每行调用erase等操作标准化首尾空白,再插入去重。

为什么 std::set 直接存 std::string 不能解决“按行去重”?
因为“按行去重”隐含两个关键前提:输入是多行文本,且每行末尾可能带 \n、\r\n 或其他空白;而 std::set<:string></:string> 会把 "hello\n" 和 "hello" 当作不同字符串。实际读入时若用 std::getline,换行符已被剥离;但若用 operator>> 或手动 read,就很可能残留。不统一处理,去重结果不可靠。
常见错误现象:std::set 输出行数比预期多,同一逻辑行出现两次(比如一次带 \r,一次不带)。
- 务必在插入前对每行做标准化:用
std::string::erase清除首尾空白,或至少调用str.erase(str.find_last_not_of(" \t\r\n") + 1)去掉右端换行和空格 - Windows 文件常用
\r\n,Linux 是\n,跨平台处理时别只删\n - 如果输入来自用户键盘输入(如
cin),std::getline已自动剥离\n,此时重点检查是否误加了额外空格
用 std::unordered_set 替代 std::set 能提升性能吗?
能,但要注意:默认的 std::unordered_set<:string></:string> 哈希函数对长字符串可能退化,且不保证输出顺序。如果你需要“首次出现的顺序输出”,std::unordered_set 无法满足——它只负责去重,不记录插入顺序。
使用场景:
– 输入行数 > 10⁴,且输出顺序无关
– 内存受限,不能额外存原始顺序
立即学习“C++免费学习笔记(深入)”;
- 启用 C++17 后,
std::unordered_set对std::string的哈希已优化,一般无需自定义哈希器 - 若担心哈希碰撞,可加
.reserve(expected_line_count)预分配桶数,避免多次 rehash - 不要用
std::set仅为了“排序输出”——除非你真需要字典序,否则按输入顺序输出更符合“按行去重”的语义
如何保留首次出现顺序并去重?
标准库没直接提供“有序去重容器”,得组合使用:std::unordered_set 判重 + std::vector 记录顺序。这是最轻量、最可控的方式。
std::unordered_set<std::string> seen;
std::vector<std::string> unique_lines;
std::string line;
while (std::getline(std::cin, line)) {
// 标准化:去掉右端空白(含 \r\n)
line.erase(line.find_last_not_of(" \t\r\n") + 1);
if (line.empty()) continue; // 可选:跳过空行
if (seen.insert(line).second) { // insert 返回 pair<iterator,bool>
unique_lines.push_back(line);
}
}
for (const auto& s : unique_lines) {
std::cout << s << '\n';
}
注意:seen.insert(line).second 是关键——insert 返回的 bool 表示是否插入成功(即此前未见过),避免重复 push。
- 不要用
if (!seen.count(line))再push_back,那样多一次哈希查找,性能差一倍 - 如果输入来自文件,把
std::cin换成std::ifstream并检查is_open() -
std::getline在遇到 EOF 或读取失败时会置位failbit,循环条件本身已处理,无需额外eof()判断
遇到中文或 UTF-8 多字节字符会乱码吗?
不会——只要你的终端、编译器、源文件编码一致(推荐 UTF-8),std::string 纯粹按字节操作,UTF-8 是向后兼容 ASCII 的,所有处理(erase、find_last_not_of、unordered_set 查找)都正常工作。
容易踩的坑:
– Windows 控制台默认是 GBK,std::cin 读入中文可能被截断或解析错
– find_last_not_of(" \t\r\n") 中的空格字符串是 ASCII 字符,对 UTF-8 安全,但别写成 find_last_not_of(" ")(全角空格),那会漏掉
- 在 Windows 上,启动时加
SetConsoleOutputCP(CP_UTF8);和SetConsoleCP(CP_UTF8);可让控制台支持 UTF-8 - Clang/GCC 编译时加
-finput-charset=UTF-8 -fexec-charset=UTF-8更稳妥 - 别用
std::wcin/std::wcout—— 它们在 Windows 上常因 locale 设置不当反而出问题
\r、要不要跳空行、要不要支持宽字符——多数情况,上面那个 unordered_set + vector 组合已经够用。


















