软工第二次作业
WordCount
Github项目地址
PSP表格:
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | 90 | 120 |
•Estimate | •估计这个任务需要多少时间 | 500 | 730 |
Development | 开发 | 40 | 30 |
•Analysis | •需求分析 (包括学习新技术) | 150 | 200 |
•Design Spec | •生成设计文档 | 30 | 20 |
•Design Review | •设计复审 | 20 | 15 |
•Coding Standard | •代码规范(为目前的开发制定合适的规范) | 10 | 20 |
•Design | •具体设计 | 10 | 20 |
•Coding | •具体编码 | 150 | 300 |
•Code Review | •代码复审 | 30 | 30 |
•Test | •测试(自我测试,修改代码,提交修改) | 20 | 20 |
Reporting | 报告 | 30 | 20 |
•Test Repor | •测试报告 | 20 | 15 |
•Size Measurement | •计算工作量 | 40 | 20 |
•Postmortem & Process Improvement Plan | •事后总结, 并提出过程改进计划 | 30 | 20 |
合计 | 580 | 730 |
解题思路描述
拿到题目首选想到的便是对文件流的操作,去查阅C++关于文件流包括头文件、类相关方法的知识。同时阅读题目要求,要求统计字符数、行数,并查找匹配的字符串作为单词做相应的输出和保存。因此除了文件流的操作,还涉及字符串的匹配与保存的问题。应该查找C++中关于字符串匹配的方法和关于存储类的相关知识。字符数和行数的话可以每次读取一行,之后统计,所以可以在自定义的文件类中用一个方法就可以实现了。可在文件类中用一个方法进行单词的匹配与提取,再用另一个方法实现对单词的出现个数统计和排序。那么整个代码的关键应该就是找出所以的单词并统计出现个数和排序,这也是这个问题的难点。所以只要找到字符串匹配的方法和存储的方法,问题就会简单许多,那么就朝着此方向查找资料。对于内部方法,除了单词整理的方法,其他方法都许打开文件读取,因此在单词整理方法上给出提示,其他方法则实现功能相对独立,不用十分复杂的流程图。
设计实现过程
由于不仅要保存单词,还要保存其出现的个数。因此可设计一个类其成员包括字符串和一个int型,在设计一个文件类,将要输出的各个值作为其成员,同时定义方法实现字符数、函数、字符串匹配等问题,有些发放在执行上有先后顺序,因此设置输出提示。在相关资料中,可以找到用正则表达式的方法来实现字符串匹配问题,同时利用C++中的list类不仅有利于存储,还提供各种方法等可用于排序。单元测试的时候是每实现一个方法就测试,确保每个方法都执行正确。
改进的思路
文件一个字符一个读不方便匹配,因此用到string提供的getline方法方便操作以及有利于字符串匹配。对于字符串中可能有多个重复的字符串单词,因此若找到匹配的单词,再在list里面查找是否已经存在则每次都要遍历、比较耗时。改进的方法是不管有无重复,都保存到list,之后利用list中sort()方法,只需遍历一遍,可以把所有单词已及其出现个数保存到存放单词的类,最后整合到一个新的list,之后再重载比较运算符利用sort()就可以很方便的实现输出前10个出现频率最高的单词。
代码说明
/*
**主要功能:
**将单词的list按字典序排序;
**整理出单词即其出现的个数放入单词对象;
**将每个单词对象加入一个新list;
*/
void File::Wordsway()
{
List::iterator i, j; /*定义迭代器,可实现对list中元素的读取*/
if (!words.empty()) /*words为存放字符串的list,判断是是否为空*/
{
words.sort(); /*对words中字符串按字典序排序*/
string s;
int k;
for (i = words.begin(); i != words.end(); ++i)
{
Word *w = new Word; /*创建一个当次类指针,开辟空间*/
s = *i;
w->name = s; /*name 为当此的字符串*/
k = 1;
for (j = ++i; j != words.end(); ++j) /*从下一个元素匹配是否和当前存入单词类中name相同,如果相同,将count加1*/
{
if (s == *j)
k++;
else
break;
}
w->count = k; /*count为单词出现的个数*/
i = --j; /*取新的当次,继续查找判断*/
words_count.push_back(*w);
}
words.unique(); /*除去words中重复的字符串*/
}
else
cout << "words list中没元素";
}
代码测试
错误提示:
输入文件名错误 输出 oper error
若未执行wordscount()方法就执行wordsway()即单词的整理方法,会显示words list中没元素。
当要执行输出文件时,如果没有执行wordscount(),也会显示words list中没元素。
当要求输入文件名时,会显示输入文件路径即名字。
测试代码:
原文件:
代码:
结果:
输出文件:
代码覆盖率:
性能测试:
收获
经过第一次任务,除了对项目要求进行充分的了解。在了解了题目要求后,设计阶段和准备阶段是十分重要的,如果事先设计好了思路和准备充分,那么实际编程除了代码调试和改善,任务完成效率会提高。而本次任务,而自己在准备阶调有了大概思路和设计就开始了编程,在编程的过程中往往有许多问题要解决,比如用正则表达式解决字符串匹配问题时,由于一次读取一行,而在没充分了解regex_search,执行过程中读一行只匹配一次,当一行有多个可匹配的时候就跳过了,后来也是不断查找相关资料,才发现regex_search匹配到字符串之后,原来的字符串是匹配截至后的部分,因此可接着匹配。所以充分了解所使用的知识对于编程时可以很熟练的解决出现的问题,这样实际编程过程中就不会花费大量的时间来解决一些基础问题。再者编程过程中加注释很是必要,当代码越写越长的时候,注释可以让我们的代码更清晰,因此良好的编程习惯和代码设计的风格对于解决问题都有帮助。