“字符串减法”实为按行去重操作:逐行读取多行字符串,用unordered_set记录已见行,保留首次出现的行并维持原序,手动处理\r换行兼容性,拼接时避免末尾多余换行。

什么是“字符串减法”:实际是去重行操作
“字符串减法”不是 C++ 标准术语,用户真正想做的是:给定一个多行字符串(含换行符),删除其中**重复的整行**,只保留第一次出现的行。这本质是「按行去重」,不是字符级减法,也不是 set_difference 那种集合差集。
关键判断:是否区分大小写?是否忽略前后空格?是否保留原始顺序?——默认按字面精确匹配、区分大小写、保留首次出现顺序。
用 std::unordered_set + std::stringstream 逐行处理
这是最常用且高效的做法:把每行当作独立字符串插入哈希集合,遇到已存在行就跳过。注意必须用 std::getline 按 \n 切分,不能用 str.split()(C++ 没这方法)。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
std::stringstream构造时传入原始字符串,std::getline(ss, line)自动处理\r\n和\n(Windows/Linux 兼容) - 用
std::unordered_set<:string></:string>记录已见行,O(1) 查找;插入前检查seen.find(line) == seen.end() - 结果拼接时,每行后加
"\n",但最后一行别多加——可用布尔标志或join思路(C++20 无原生 join,手动处理更稳)
std::string dedupe_lines(const std::string& input) {
std::stringstream ss(input);
std::string line;
std::unordered_set<:string> seen;
std::string result;
bool first = true;
while (std::getline(ss, line)) {
if (seen.find(line) == seen.end()) {
seen.insert(line);
if (!first) result += "\n";
result += line;
first = false;
}
}
return result;
}</:string>
遇到空行或含 \r 的文件会出错吗?
会。如果输入来自 Windows 文本文件,std::getline 默认以 \n 为分隔符,读到 "abc\r" 就算一行——\r 会留在 line 末尾,导致 "abc" 和 "abc\r" 被视为不同行。
- 解决办法:读取后手动去除行尾
\r,用line.erase(line.find_last_not_of("\r") + 1)或更安全的循环 trim - 空行(即
line == "")会被正常去重——若你希望忽略空行,加判断if (line.empty()) continue; - 若需跨平台健壮处理,建议先统一替换
"\r\n"为"\n",再用std::getline
性能与内存注意事项
对百万行文本,std::unordered_set 的哈希计算和内存分配可能成瓶颈;若行很长(如每行 10KB),重复字符串存储浪费内存。
- 优化方向:改用
std::unordered_set<size_t></size_t>存哈希值(如std::hash<:string>{}(line)</:string>),但有哈希碰撞风险,不推荐生产环境 - 更稳妥做法:用
std::vector<:string></:string>缓存唯一行,查找用std::find(O(n)),适合小数据;大数据仍用unordered_set,并预留容量:seen.reserve(expected_unique_count) - 注意:
std::string移动语义在 C++11+ 下自动生效,result += line不一定触发深拷贝,但频繁拼接仍建议用std::ostringstream或预估长度result.reserve(total_size_hint)
真正难的不是写对逻辑,而是搞清输入来源的换行风格、空格处理粒度、以及是否要支持正则模糊匹配——这些需求一变,上面所有代码都得重审。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










