福大软工1816 · 第二次作业 - 个人项目
github地址
https://github.com/zimengxueying/personal-project/tree/master/Cplusplus
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟)20 | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 10 | 20 |
· Estimate | · 估计这个任务需要多少时间 | 20 | 20 |
Development | 开发 | 120 | 130 |
· Analysis | · 需求分析 (包括学习新技术) | 180 | 300 |
· Design Spec | · 生成设计文档 | 20 | 30 |
· Design Review | · 设计复审 | 10 | 0 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 180 |
· Design | · 具体设计 | 30 | 20 |
· Coding | · 具体编码 | 600 | 720 |
· Code Review | · 代码复审 | 20 | 100 |
· Test | · 测试(自我测试,修改代码,提交修改) | 120 | 60 |
Reporting | 报告 | 120 | 150 |
· Test Repor | · 测试报告 | 20 | 60 |
· Size Measurement | · 计算工作量 | 30 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 60 | 10 |
| | 合计 | 1660|1800
2.解题思路
根据题目的要求需要实现下面的功能
1 文本读入功能。利用ifstream可以实现,但在具体写代码的时候我在getline还是iftream读入的时候我还是有纠结的
2 统计行数。但是题目中要求的是要统计非空白行,所以要加条件利用getline函数逐行读取,将换行符和'\t'去掉之后统计剩下的的长度>0行数+1
3统计字符数。也是有要求不能是中文一类的,加一个ascii码的条件就好了
4 统计单词数。题目中的要求:
(1)以4个英文字母 A-Z,a-z 开头,后续可以是字母和数字A-Z, a-z,0-9。file123是一个单词,123file不是一个单词。(换言之,如果有一行内容是123file,那么“单词”是file。)(2)分割符是非字母数字符号,空格。 一开始还是挺头疼的,准备从这种单词的规律入手,花了很多时间写了一段代码,自认为逻辑没有什么问题,但就是输出不了结果,卡了很长时间的bug之后经大佬的提醒有一种叫正则表达式的神奇的东西.那就显得很轻松了
5 将频率前十的输出出来。并且在频率相同时按照字典顺序输出原本在统计单词数的时候我将单词及其对应的次数存在map里面,然后sort一下即可。
3 设计实现过程
CountChar.h 用于实现字符计数
CountLines.h 用于实现计算行数
CountWordnumber.h 用于计算有效单词数
TenFrequency.h 用于将频率前十的输出出来
要实现这些功能有些其实并不难(但由于我挺菜还是花了一点时间的)这里我觉得最关键的其实是统计单词的数目,也就是我上面CountWordnumber.cpp实现的内容
(其实思路挺简单,但是有关map以及正则表达式是需要看的)
功能测试部分
性能分析
由vs 2017生成的性能分析图
关键代码展示
1 首先有关读取文件
我在统计字符以及计算单词数的时候读取的方式不太一样
ifstream ifs(mInputFileName); //这里按照单个字符读取并判断会比较简洁
char charTemp;
while ((charTemp = ifs.get()) != EOF)
string mStrTemp;
ifstream ifs(mInputFileName); //自动会将空格和换行去掉更容易写正则表达式
while (ifs >> mStrTemp)
2 单词计数
int CountWordnumber(string mInputFileName)
{
string mStrTemp;
map<string, int> mCountMap;
regex regWordPattern("^[a-z]{4}[a-z0-9]*");//单词的正则式
ifstream ifs(mInputFileName);
while (ifs >> mStrTemp) {//大写转化为小写
int len = mStrTemp.length();
for (int i = 0; i < len; i++) {
if (mStrTemp[i] >= 65 && mStrTemp[i] <= 90)
mStrTemp[i] = mStrTemp[i] + 32;
}
const std::sregex_token_iterator end;
for (sregex_token_iterator wordIter(mStrTemp.begin(), mStrTemp.end(), regWordPattern); wordIter != end; wordIter++) {//在一行文本中逐个找出单词
//cout<<*wordIter<<endl;//每个单词
mCountMap[*wordIter]++;//单词计数
}
}
ifs.clear();
ifs.seekg(0);
map<string, int>::const_iterator map_it;
int sum = 0;
for (map_it = mCountMap.begin(); map_it != mCountMap.end(); map_it++)
{
sum += map_it->second;
}
return sum;
}
3频率最高的十个单词
bool sortWord(pair<string, int> elem1, pair<string, int> elem2)
{
return elem1.second > elem2.second;
}
void TenFrequency(string mInputFileName)
{
string mStrTemp;
map<string, int> mCountMap;
regex regWordPattern("^[a-z]{4}[a-z0-9]*");//单词的正则式
ifstream ifs(mInputFileName);
while (ifs >> mStrTemp) {//大写转化为小写
int len = mStrTemp.length();
for (int i = 0; i < len; i++) {
if (mStrTemp[i] >= 65 && mStrTemp[i] <= 90)
mStrTemp[i] = mStrTemp[i] + 32;
}
const std::sregex_token_iterator end;
for (sregex_token_iterator wordIter(mStrTemp.begin(), mStrTemp.end(), regWordPattern); wordIter != end; wordIter++) {//在一行文本中逐个找出单词
//cout<<*wordIter<<endl;//每个单词
mCountMap[*wordIter]++;//单词计数
}
}
ifs.clear();
ifs.seekg(0);
vector<pair<string, int>> word;
for (map<string, int>::iterator iter = mCountMap.begin(); iter != mCountMap.end(); iter++) {
word.push_back(pair<string, int>(iter->first, iter->second));
}
sort(word.begin(), word.end(), sortWord);
int size = 10;
if (word.size() < 10) {
size = word.size();
cout << "单词不超过10个" << endl;
}
vector<pair<string, int>>::iterator vit;
for (vit = word.begin(); vit != word.begin() + size; vit++) {
cout << "<" << vit->first << ">:" << " " << vit->second << endl;
}
}
性能分析和改善
单元检测如下
其中的一些函数
1 实现CountWordnumber和Tenfrequency的时候读了两遍文件,虽然都是O(n)级别的但是还是有点影响,并且考虑到后面封装成dll怕出错就将全局变量去掉,保险起见写了两遍。
2 实现十个最高频率输出的时候看到有大佬用到堆,理论上找出前十的时间是O(logn)比sort快多了。
代码覆盖率如下
一般来说代码覆盖率都有八九十不知道为什么我的这么低,一开始以为自己电脑的原因后来是发现不是。单元测试也做过了..不解= =
异常处理
当单词数小于10个
···
int size = 10;
if (word.size() < 10) {
size = word.size();
cout << "单词不超过10个" << endl;
}
···
读入文件
···
ifstream file;
file.open(argv[i], ios::in);
if (!file) {
cout << "无法打开文件\n";
return -1;
}
file.close();
···
总结
1 首先要审好题。之前对于单词的定义没有完全搞懂分隔符,自己造了轮子写了一大通自认为逻辑是对的东西。
2 由于自己没有工程文件的意识,一开始自己的代码全都写在一个main.cpp中,没有看清题目要求而且后面要生成.dll文件也要独立出来。一开始认为很简单但是实践操作由于之前写的太少,手也生疏,其实也是卡了一段时间的。虽然最后没有用有关全局变量的东西,但是把一些建立工程时的注意事项搞清楚了。
3 发现这次的作业真的不是写好代码就行的,后面的的性能分析和覆盖率也很重要但是由于前面写代码以及补c++的map vector的用法消耗了太多时间,时间没有分配好,导致后面的性能分析也只停留在理论上
4 最后就是做作业是一个苦练心态的过程,会出现很多莫名其妙的错误有的时候甚至百度不到,心态还不能崩,不然就来不及做。总之是一个很煎熬的过程。总之,后面的实践会越来越虐,还是要保持好心态= =