用std::map统计字符频次最直观,自动初始化计数、按键ASCII升序输出;需注意大小写区分,忽略时应先转tolower;对纯ASCII字符串,std::array<int,256>更快更省内存,但须转unsigned char下标。

用 std::map<char int></char> 统计字符频次最直观
直接映射字符到计数,插入和更新逻辑清晰,适合初学或小规模字符串。注意 std::map 默认按字符 ASCII 码升序遍历,如果只关心计数不关心顺序,它比 std::unordered_map 稍慢但更稳定。
常见错误是忘记初始化计数器——其实不用手动初始化:map[c]++ 会自动将未出现的 c 对应值设为 0 再加 1。
- 对空字符串或含控制字符(如
'\0'、'\t')的字符串也有效 - 区分大小写:
'A'和'a'被视为不同字符 - 若需忽略大小写,先统一转成
std::tolower(c)
std::map<char, int> count;
for (char c : s) {
count[c]++;
}
用 std::unordered_map<char int></char> 提升大量数据性能
当字符串长度超 10⁴ 或需高频调用时,哈希表平均 O(1) 插入比红黑树 O(log n) 更快。但要注意:键类型必须支持哈希(char 原生支持),且遍历顺序不可预测。
容易踩的坑是误以为 unordered_map 的迭代器顺序和插入顺序一致——它不保证,C++20 也没改这点。如果后续要按输入顺序输出频次,得额外存索引或改用 std::vector<std::pair<char, int>> + 手动去重。
立即学习“C++免费学习笔记(深入)”;
- 内存占用略高,因哈希桶预分配空间
- 极端情况下哈希冲突多,退化为链表,但
char只有 256 种可能,冲突概率极低 - 若字符串只含 ASCII 字符,可考虑用
std::array<int, 256>替代,更快更省内存
用 std::array<int></int> 处理纯 ASCII 字符串最快
当确定输入全是 ASCII(0–255),数组下标即字符值,访问 O(1),无哈希开销,无内存动态分配。这是真正意义上的零成本抽象。
关键点在于:必须把 char 强转为 unsigned char 再作下标,否则负值(如某些平台 char 默认 signed)会导致越界访问。
- 初始化必须全为 0:
std::array<int, 256> count{};(花括号初始化清零) - 不能用于 UTF-8 多字节字符——一个中文字符占多个字节,会被拆成多个独立计数
- 若需统计 Unicode 码点,得先解码 UTF-8,再用
std::unordered_map<char32_t, int>
std::array<int, 256> count{};
for (unsigned char c : s) {
count[c]++;
}
遇到中文或 UTF-8 字符串怎么办
标准库没有内置 UTF-8 字符计数,std::string 的 operator[] 按字节索引,不是按字符。直接遍历会把一个汉字当 3 个字节分别计数。
正确做法是先用轻量级 UTF-8 解码逻辑识别字符边界,提取每个 char32_t 码点,再统计。别依赖第三方库(如 ICU)除非项目已引入;可用 utf8cpp 这类 header-only 库,或手写解码(仅需处理 1–4 字节序列)。
- Windows 控制台默认 ANSI 编码,读取含中文的文件可能乱码,先确认源字符串编码
-
std::wstring和wchar_t在各平台宽度不一(Windows 是 16-bit,Linux/macOS 是 32-bit),不推荐作为通用方案 - 若只是简单需求(如日志分析),先转 UTF-8 再用
utf8::next()(来自 utf8cpp)逐字符迭代
unordered_map,也别在处理用户昵称时硬套 array<256>。


















