C++ 统计单词出现次数

C++ 统计单词出现次数

十月 02, 2018

前言

咱们云计算课程的第一次实验:给定一英文文本文件 data.dat,编写 C 程序,读取文件中的内容,统计文件中出现次数最多的三个单词,并给出这三个单词的出现次数,同时输出程序运行的时间。(注: 这里不区分单词大小写,例如, he 与 He 当做是同一个单词计数)

C 实在是不会用,直接用 C++ 好了……趴。

思路

  1. 思路其实并没有多复杂,第一反应就是用 map 了,用键值对存储单词及其对应的出现次数,然后按照键值对给 map 排序,最后输出出现次数排在前三位的单词及其次数。

  2. 由于 map 会区分单词大小写,即 HE,He,he,hE会被分开存储,于是就需要在输入单词之后将其都转化为小写,再存储进 map 里面。

实现

包含的头文件

1
2
3
4
5
6
7
8
9
#include <iostream> //cout 输出流
#include <fstream> //读取文件流
#include <iomanip> //控制输出格式
#include <map> //存储单词
#include <vector> //用于排序
#include <algorithm> //sort 排序算法
#include <time.h> //计算程序运行时间
#include <string> //字符串操作
#include <cctype> //过滤标点符号

map 的用法

1
2
3
4
5
6
7
8
9
10
11
map<string, size_t> wordCount;
vector<pair<string, size_t>> wordVector;
ifstream inFile;
inFile.open("data.dat");
while (!inFile.eof())
{
inFile >> word;
word = delePunct(word);
word = toLower(word);
wordCount[word]++;
}

这里使用
map<string, size_t> wordCount;
定义了一个名为wordCountmap,其中第一个数据(关键字 key)为 string 类型,用于存储每一个单词,第二个数据(值 value )为 size_t类型(C++ Primer 第五版 P375)。

注:在使用数组下标时,通常将其定义为 size_t类型,size_t是一种机器相关的无符号类型,它被设计得足够大 以便能够表示内存中任意对象的大小。(C++ Primer 第五版 P103)

接着通过while循环每次从inFile中输入一个单词,并将其去掉标点符号(delePunct())和转换成小写(toLower())之后,将其存入到map当中,并使其对应的值加 1 (若该单词不存在则会自动创建一个新的关键字)。

去掉标点符号

1
2
3
4
5
6
7
8
9
10
11
12
string delePunct(string word)
{
string outword;
for (int i = 0; i < word.size(); i++)
{
if (!ispunct(word[i]))
{
outword = outword + word[i];
}
}
return outword;
}

将单词全部转换成小写

1
2
3
4
5
6
7
8
string toLower(string word)
{
for (int i = 0; i < word.size(); i++)
{
word[i] = tolower(word[i]);
}
return word;
}

使用 for 循环输出 map

1
2
3
4
5
for (auto &w : wordCount)
{
cout << left << setw(20) << w.first << "\t" << w.second
<< ((w.second > 1)? " times" : " time") << endl;
}

将 map 按值排序并输出

1
2
3
4
5
6
7
8
9
10
11
12
13
void sortByValue(map<string, size_t> &wordMap, vector<pair<string, size_t>> &wordVector)
{
for (auto &iter : wordMap)
{
wordVector.push_back(make_pair(iter.first, iter.second));
}
sort(wordVector.begin(), wordVector.end(), compare);
for (auto iter = wordVector.begin(); iter < (wordVector.begin() + 3); iter++)
{
cout << left << setw(20) << iter->first << "\t" << iter->second
<< ((iter->second > 1) ? " times" : " time") << endl;
}
}

值得注意的是,C++ 语言中的 map 默认按照 key 排序,并且也没有内建按值(value)排序的函数,所以我们需要将 map 中存储的 pair 对象存放在一个新建的 vector 中,然后再使用 vector 中的 sort 函数对其进行排序。
vector 使用的 sort 函数中第三个参数 compare

1
2
3
4
bool compare(const pair<string, size_t> &x, const pair<string, size_t> &y)
{
return x.second > y.second;
}

完整源代码
(并附有 Java 版本)