手动用 find + substr 切分字符串最可控高效:需错开更新 pos 和 comma_pos 避免死循环,每次截取后立即 trim(find_first_not_of + find_last_not_of)并跳过空串,末尾段单独处理,超长字符串推荐 string_view + unordered_set 配自定义哈希。

std::string 用 find + substr 手动切分最可控
想靠 std::stringstream 或 std::regex 按逗号分割?容易踩空格、连续逗号、首尾空格的坑,而且 regex 性能差、编译慢。手动用 find 和 substr 是最快也最稳的方式——不依赖额外库,不触发临时字符串拷贝(C++17 起 substr 返回的是视图或小字符串优化后内存局部性好)。
关键点:pos 和 comma_pos 要错开更新,避免死循环;每次提取前用 substr 截取后,立刻调用 find_first_not_of(" \t\n\r") 去首尾空白再插入 std::set。
- 连续逗号(
"a,,b")会产生空串,必须跳过 - 原始字符串末尾无逗号时,最后一段要单独处理
- 如果输入量大(>10k 字符),避免在循环里反复调用
trim函数——直接用两个指针前后扫描更省
std::set 插入前必须 trim,否则 "abc" 和 "abc " 被视为不同元素
std::set 的比较是严格字节级的,空格算有效字符。不 trim 就直接 insert,会导致去重失效。别指望 std::set<:string></:string> 自带“语义去重”——它只认 operator<,而 "x " < "x" 为 true(因空格 ASCII 码 32 小于字母)。
推荐 inline trim:用 find_first_not_of 找左边界,find_last_not_of 找右边界,再用 substr 截取。不要用 boost::trim 或自定义 trim 函数——增加依赖且可能隐式分配。
立即学习“C++免费学习笔记(深入)”;
- trim 后判断长度是否为 0,是则跳过 insert(对应空字段)
- 如果业务允许忽略大小写去重,得用
std::set自定义比较器,不能靠事后 tolower ——那样 insert 两次才生效,浪费 -
std::unordered_set更快,但需提供哈希和等价判断;若只需去重+遍历有序,std::set反而省事
性能敏感场景改用 std::unordered_set + string_view(C++17)
当输入字符串超长(如 >1MB)或分割频次极高(如每秒千次),std::string 构造开销明显。C++17 的 std::string_view 配合 std::unordered_set<std::string_view> 可避免所有堆分配——前提是源字符串生命周期长于 set 存活期。
注意:std::string_view 不能直接用于 std::unordered_set,因为默认哈希没定义。必须传入自定义哈希函数,比如用 std::hash<std::string_view>{}(标准已支持),并确保 key 指向内存不被提前释放。
- 若源字符串是局部变量(如函数参数),绝不能存
string_view到全局 set 中 - 用
std::unordered_set时,等价比较器也要用std::equal_to<std::string_view>,别漏掉 - insert 前仍需 trim,但可用
string_view的remove_prefix/remove_suffix零拷贝完成
连续逗号和首尾空格导致 set 多出空字符串?检查 substr 边界
典型错误代码:substr(pos, comma_pos - pos) 在 comma_pos == std::string::npos 时会截取到极大长度,触发越界或意外空串。正确做法是先算长度:size_t len = (comma_pos == std::string::npos) ? str.size() - pos : comma_pos - pos;,再判断 len == 0 跳过。
另一个隐形坑:用 while ((comma_pos = str.find(',', pos)) != std::string::npos) 时,若 pos 没更新到 comma_pos + 1,就会卡在同一个位置反复 find 到同一个逗号。
- 每次循环结尾必须写
pos = comma_pos + 1; - 循环外补一句
if (pos < str.length()) { /* 处理最后一段 */ } - 调试时打印每段长度和内容,比看最终 set size 更早发现空串来源
实际中最容易被忽略的,是 trim 和边界判断必须同步做——不是“先切再 trim”,而是“切完立刻判空再 trim 再判空”,少一层检查就可能让空字符串混进 set。


















