应使用 std::unordered_map 统计单词频次,先清洗文本(替换非字母字符为空格、转小写),逐行读取避免内存溢出,跳过空字符串;UTF-8 文件需谨慎处理多字节字符。

用 std::map 统计单词频次,但要注意分词边界
直接按空格切分会出错——标点、换行、大小写混在一起时,"hello" 和 "hello," 会被当成两个词。实际得先清洗:把非字母字符(如逗号、句号)替换成空格,再统一转小写。
- 读文件用
std::ifstream,逐行读比一次性加载更安全(大文件不爆内存) - 用
std::istringstream拆每行,避免手写分割逻辑 - 对每个单词调用
std::tolower逐字符转换,别用std::transform+std::toupper混用 - 跳过空字符串(连续空格或行首尾空格会导致空 token)
std::unordered_map 比 std::map 更快,但无法按字母序输出
如果只关心频次、不care打印顺序,用 std::unordered_map<:string int></:string>;插入和查找平均 O(1),而 std::map 是 O(log n)。但后者天然有序,调试时肉眼扫更方便。
- 初始化:
std::unordered_map<:string int> word_count;</:string> - 计数:
word_count[word]++;—— 这句自动初始化为 0 再加 1,不用提前检查是否存在 - 若需按字母排序输出,最后把 key 拷进
std::vector再std::sort,别边插边排
处理 Unicode 或 UTF-8 编码的文件要格外小心
标准 C++ iostream 默认按字节处理,遇到中文或带重音的字母(如 café)会切碎成多个无效字符。纯英文 ASCII 文件没问题;一旦文件是 UTF-8 编码且含非 ASCII 字符,std::string 能存,但 std::tolower 对多字节字符无效,可能把 é 变成乱码。
- 确认文件编码:Linux/macOS 用
file -i filename,Windows 记事本另存时看“编码”下拉框 - 纯英文场景下,UTF-8 和 ASCII 兼容,可放心用;含西欧字符建议用 ICU 库,C++20 的
<text></text>还没普及 - 简单规避法:遇到非
isalpha(static_cast<unsigned char>(c))</unsigned>的字符就当分隔符,不尝试 decode
内存和性能边界:大文件别用 std::getline 无限制读
单行超几 MB 的日志(比如 minified JSON 塞进一行)会让 std::getline 分配巨量内存,甚至触发 bad_alloc。得设缓冲区或改用字符流逐个读。
立即学习“C++免费学习笔记(深入)”;
- 稳妥做法:用
char buf[4096];配合in.read(buf, sizeof(buf)-1)手动拼接,但增加复杂度 - 折中方案:检查每行长度,超过阈值(如 100KB)就 warn 并跳过该行——多数文本文件单行不会这么长
- 词频本身不依赖上下文,所以按块读、按行切、按词计,三层解耦后更容易定位瓶颈
真正麻烦的是混合格式:既有正常段落,又有 base64 编码块或 XML 注释嵌套。这时候光靠正则或空格分割根本不可靠,得结合业务规则做预处理——但那是另一个问题了。


















