最简洁方式是用迭代器构造:std::set charset(s.begin(), s.end()),自动去重排序;需忽略大小写时提供自定义比较器;支持空字符串和'\0',但c_str()会截断。

直接用迭代器构造 std::set 最简洁
不需要手动遍历字符串,std::set 的迭代器构造函数能直接消费 std::string 的字符范围。这是最常用、最安全的方式,自动去重并排序:
std::string s = "hello";
std::set<char> charSet(s.begin(), s.end()); // 得到 { 'e', 'h', 'l', 'o' }
</char>
注意:构造后元素已按 char 的 ASCII 值升序排列,且重复字符(如两个 'l')只保留一个。
如果需要忽略大小写或自定义比较逻辑
默认构造使用 std::less<char></char>,区分大小写('A' 和 'a' 被视为不同字符)。若需不区分大小写,必须提供自定义比较器:
struct IgnoreCaseCompare {
bool operator()(char a, char b) const {
return std::tolower(static_cast<unsigned char>(a)) (b));
}
};
std::set<char ignorecasecompare> charSet(s.begin(), s.end());
</char></unsigned>
- 必须对
std::tolower的参数做unsigned char强转,否则传入负值(如某些 locale 下的扩展字符)会触发未定义行为 - 比较器类型是
std::set模板的第二个参数,不能事后修改 - 这种 set 里
'A'和'a'仍算两个不同 key——它只是让比较时“看起来一样”,但实际存储仍是原始字符;真要统一存小写,得预处理字符串
遇到空字符串或含 null 字符 '\0' 怎么办
std::string 允许包含 '\0',且 .begin()/.end() 迭代器会如实遍历所有字符,包括中间的 '\0'。这和 C 风格字符串不同:
std::string s = "a\0b"; // 长度为 3 std::set<char> chars(s.begin(), s.end()); // 包含 'a', '\0', 'b' </char>
- 空字符串构造出空
std::set,无异常,无需额外判空 - 若业务上想跳过
'\0',得先过滤:std::string filtered; std::copy_if(s.begin(), s.end(), std::back_inserter(filtered), [](char c) { return c != '\0'; }); - 不要用
s.c_str()构造——它会在首个'\0'截断,丢失后续字符
性能与适用场景提醒
每次构造都涉及 N 次红黑树插入(N 是字符串长度),平均 O(N log N)。如果只是临时查某个字符是否在字符串中出现过,更高效的做法是:
- 对短字符串(比如长度 std::find(s.begin(), s.end(), target) —— O(N),常数小
- 对长字符串且需多次查询,才值得构建
std::set或std::unordered_set - 如果只关心“存在性”且不需排序,
std::unordered_set<char></char>插入和查找平均 O(1),但失去顺序性
真正需要 std::set<char></char> 的典型场景是:既要去重,又要按字符顺序遍历(比如生成字母表差集、有序输出唯一字符)。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











