用std::unordered_map统计词频最直接高效,需正则分词并转小写,归一化或截断频率后用setw和string生成ascii直方图。

用 std::unordered_map 统计词频最直接,但要注意分词边界
直接用 std::unordered_map<:string int></:string> 存词和次数没问题,但“词”定义不明确会导致统计失真。英文通常按空格+标点切分,中文需额外处理(本文默认英文或空格分隔文本)。别用 std::map——它强制排序,纯浪费时间;unordered_map 平均 O(1) 插入查找,实测快 2–3 倍。
常见错误是用 std::stringstream 简单 operator>> 读单词,会吞掉连续空格、丢掉行首/尾词,还无法过滤标点。建议用 std::regex 或手动遍历:
std::regex word_regex(R"(\b[a-zA-Z]+\b)");
auto words_begin = std::sregex_iterator(text.begin(), text.end(), word_regex);
auto words_end = std::sregex_iterator();
for (std::sregex_iterator i = words_begin; i != words_end; ++i) {
std::string word = i->str();
std::transform(word.begin(), word.end(), word.begin(), ::tolower);
freq[word]++;
}
生成直方图前先做频率归一化或截断,否则小词淹没在大词里
原始频次范围可能极大(比如 “the” 出现 500 次,“algorithm” 只有 3 次),直接画柱状长度会失真。两种实用策略:
- 取 top-K(如 K=20),只画高频词——用
std::partial_sort_copy配自定义比较器,比全排序快 - 线性缩放到固定宽度(如 50 字符),公式:
int bar_width = std::min(50, (freq[word] * 50) / max_freq);注意max_freq为 0 时要防除零 - 若想视觉更均衡,改用对数缩放:
int bar_width = static_cast<int>(log2(freq[word] + 1) * 8)</int>,加 1 避开 log(0)
输出 ASCII 直方图时,std::setw 和 std::left 控制对齐更可靠
别手写循环拼空格——易错且难维护。用 <iomanip></iomanip> 的 std::setw 和 std::left 对齐词名,再用 std::string(bar_width, '#') 生成条形:
for (const auto& [word, count] : top_words) {
int bar_width = std::min(40, static_cast<int>((count * 40) / max_count));
std::cout <p>注意:如果词长超 16,<code>std::setw(16)</code> 不会截断,而是撑开字段,导致后续对齐错位。稳妥做法是先 <code>word.substr(0, 15) + "."</code> 截断,或动态算最大词长再设宽。</p>
<h3>性能敏感场景下,避免重复构造 <code>std::string</code> 和小对象拷贝</h3>
<p>高频调用时,每次 <code>freq[word]++</code> 会触发 <code>std::string</code> 拷贝;若文本巨大,可改用 <code>std::string_view</code>(C++17)作 key:</p>
<pre class="brush:php;toolbar:false;">std::unordered_map<:string_view int> freq;
// 注意:string_view 必须指向生命周期长于 map 的字符串,比如 const std::string& text</:string_view>
但 std::string_view 不能直接用于 std::regex_iterator 结果(其 i->str() 返回临时 std::string),得先存到 std::vector<:string></:string> 再转 view,权衡取舍。另外,VS2019 及更早版本对 string_view hash 支持不完整,编译报错时回退用 std::string。
真正卡顿往往不在统计本身,而在输出——尤其终端渲染大量 # 字符时。如果直方图只是调试用,考虑只输出前 10 行;生产环境导出 CSV 或交给 gnuplot 更实际。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











