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++17 后,
std::unordered_set对std::string的哈希已优化,一般无需自定义哈希器 - 若担心哈希碰撞,可加
.reserve(expected_line_count)预分配桶数,避免多次 rehash - 不要用
std::set仅为了“排序输出”——除非你真需要字典序,否则按输入顺序输出更符合“按行去重”的语义
如何保留首次出现顺序并去重?
标准库没直接提供“有序去重容器”,得组合使用:std::unordered_set 判重 + std::vector 记录顺序。这是最轻量、最可控的方式。
std::unordered_set<:string> seen;
std::vector<: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>
unique_lines.push_back(line);
}
}
for (const auto& s : unique_lines) {
std::cout
<p>注意:<code>seen.insert(line).second</code> 是关键——<code>insert</code> 返回的 <code>bool</code> 表示是否插入成功(即此前未见过),避免重复 push。</p>
<ul>
<li>不要用 <code>if (!seen.count(line))</code> 再 <code>push_back</code>,那样多一次哈希查找,性能差一倍</li>
<li>如果输入来自文件,把 <code>std::cin</code> 换成 <code>std::ifstream</code> 并检查 <code>is_open()</code>
</li>
<li>
<code>std::getline</code> 在遇到 EOF 或读取失败时会置位 <code>failbit</code>,循环条件本身已处理,无需额外 <code>eof()</code> 判断</li>
</ul>
<h3>遇到中文或 UTF-8 多字节字符会乱码吗?</h3>
<p>不会——只要你的终端、编译器、源文件编码一致(推荐 UTF-8),<code>std::string</code> 纯粹按字节操作,UTF-8 是向后兼容 ASCII 的,所有处理(<code>erase</code>、<code>find_last_not_of</code>、<code>unordered_set</code> 查找)都正常工作。</p>
<p>容易踩的坑:<br>– Windows 控制台默认是 GBK,<code>std::cin</code> 读入中文可能被截断或解析错<br>– <code>find_last_not_of(" \t\r\n")</code> 中的空格字符串是 ASCII 字符,对 UTF-8 安全,但别写成 <code>find_last_not_of(" ")</code>(全角空格),那会漏掉</p>
<ul>
<li>在 Windows 上,启动时加 <code>SetConsoleOutputCP(CP_UTF8);</code> 和 <code>SetConsoleCP(CP_UTF8);</code> 可让控制台支持 UTF-8</li>
<li>Clang/GCC 编译时加 <code>-finput-charset=UTF-8 -fexec-charset=UTF-8</code> 更稳妥</li>
<li>别用 <code>std::wcin</code>/<code>std::wcout</code> —— 它们在 Windows 上常因 locale 设置不当反而出问题</li>
</ul>
实际写的时候,最麻烦的不是算法,是输入来源和编码环境的不确定性。先确认你读的是什么、在哪跑、要不要兼容旧系统,再决定删不删 <code>\r</code>、要不要跳空行、要不要支持宽字符——多数情况,上面那个 <code>unordered_set</code> + <code>vector</code> 组合已经够用。</iterator></:string></:string>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











