2018福大软工实践第二次作业

Github项目地址

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划3020
· Estimate· 估计这个任务需要多少时间3020
Development开发600700
· Analysis· 需求分析 (包括学习新技术)180150
· Design Spec· 生成设计文档4030
· Design Review· 设计复审1010
· Coding Standard· 代码规范 (为目前的开发制定合适的规范)3060
· Design· 具体设计4060
· Coding· 具体编码180180
· Code Review· 代码复审6030
· Test· 测试(自我测试,修改代码,提交修改)60180
Reporting报告210220
· Test Repor· 测试报告6090
· Size Measurement· 计算工作量3010
· Postmortem & Process Improvement Plan· 事后总结, 并提出过程改进计划120120
合计840940

解题思路:

  • 首先,明确命令行程序WordCount要实现的五个功能:统计文件的字符数、统计文件的单词总数(单词:至少以4个英文字母开头,跟上字母数字符号)、统计文件的有效行数、统计文件中各单词的出现次数,输出频率最高的10个、按字典序输出到文件result.txt。其次,每条功能分析,设计解决办法。对于文件字符数,将文件内容赋值给一个string类型的变量strTxt,使用.length()即可求出文件字符数;对于文件的有效行数,定义int类型变量len,设初始值为1,每两个换行符中间包含有非空字符则len++,最后一行特殊考虑;对于文件的单词总数,将strTxt中非单词的字符置为空格,即可分离出单词,统计单词总数,然后使用unordered_map容器储存单词和单词出现的频率<string, int>;对于单词的排序输出,借助vector容器使用sort()函数即可完成排序,输出则是使用输出文件流将结果输出到文件result.txt。
  • 在该题目设计解决方案的过程中,对于我来说,统计文件单词出现频率这个功能耗时比较久。一开始我并没有想到使用容器,我想到的是一个string数组,对应一个int数组,但是考虑到如果使用两个数组来存储,那么后面的排序什么的代码实现起来会比较复杂,所以我上网查找了具有一个string对应一个int功能的数据存储方式,找到了unordered_map容器正好适用于题目的情景,所以临时学习了一下这个容器的概念和使用,应用于本题的储存。

设计实现:

  • WordCount主要设计了3个函数,readTxt()用于读入文本并将大写字母改为小写后赋值给string类型变量strTxt,countWords()用于统计文本有效行以及单词总数和频率,writeTxt()用于输出结果文本result.txt。readTxt()在读入文本时是一个一个字母读入的,且改为小写后再接到strTxt后,因此之后在统计相同单词的时候不需考虑大小写问题;countWords()的关键在于使用哈希容器unordered_map存储key-value,通过key(单词)快速索引到value(频率),此外通过C++自带的sort()函数借助vector容器对key-value进行排序,排序根据题目所给规则,找到出现频率最高的10个;writeTxt()简单使用输出文件流将结果输出到文件result.txt。
总的流程图:

1476119-20180912012134079-1454605945.png

统计词频的流程图:

1476119-20180912012148733-1947784218.png

类图:

1476119-20180912012205463-603482548.png

函数关系图:

1476119-20180912013752344-1998231236.png

代码说明:

统计词频的代码:
void  Core::wordHigh(string str) {
    unordered_map<string, int> strMap = createMap(str);
    for (auto it = strMap.begin(); it != strMap.end(); it++) {                                            
//使用迭代器将unordered_map容器中的数据拷贝到vector容器中
        vtMap.push_back(make_pair(it->first, it->second));                                       
    }
    sort(vtMap.begin(), vtMap.end(), myFunc);                                                             
 //使用vector容器进行排序
}

unordered_map<string, int> createMap(string str) {
    unordered_map<string, int> strMap;
    string strWords;
    stringstream strm(strManage(str));                                      
//使用unordered_map容器存储<string,int>对
    while (strm >> strWords) {
        unordered_map<string, int>::iterator it = strMap.find(strWords);
        if (it == strMap.end()) {                                                                                    
 //容器中没有找到单词则将单词存入容器,int值设为1
            strMap.insert(unordered_map<string, int>::value_type(strWords, 1));
        }
        else {                                                                                                                
//容器中找到单词则相对应的int值自加1
            strMap[strWords]++;
        }
    }
    return strMap;
}

bool myFunc(const pair<string, int> &x, const pair<string, int> &y) {                                
//用于定义sort()函数的比较规则
    if (x.second > y.second)
        return true;
    else if (x.second == y.second) {
        if (x.first < y.first)
            return true;
        else
            return false;
    }
    else if (x.second < y.second)
        return false;
}
  • 在统计词频的实现中,我先用unordered_map容器存储<单词,词频>对,再将数据拷贝到vector容器中,在vector容器中使用sort()函数对<单词,词频>对进行排序。使用unordered_map容器存储<单词,词频>对是因为该容器内部实现了哈希表,查找速度非常快,而每个单词都需要查找一遍容器,因此选择该容器存储<单词,词频>对。而由于unordered_map容器中使用hash组织内容,内部是无序的,所以我选择将该容器中的数据拷贝到vector容器中使用sort()函数进行排序,再根据题目要求编写比较规则即可实现排序功能。

性能改进:

1476119-20180910232531526-1755442901.png
1476119-20180910232538580-289401003.png

消耗最大的函数代码:
void writeTxt() {
    ofstream outfile("result.txt", ios::out);
    if (!outfile) {
        cerr << "open error!" << endl;
        exit(1);
    }
    int t = 0;
    outfile << "characters: " << characters << endl;
    outfile << "words: " << words << endl;
    outfile << "lines: " << lines << endl;
    for (auto it = vtMap.begin(); t<10 && it != vtMap.end(); ++it, t++) {
        outfile << "<" << it->first << ">: " << it->second << endl;
    }
    outfile.close();
}

单元测试:

1476119-20180911000306522-1311104509.png

  • 根据题目的要求可以测试以下情况:包含空白字符的无效行、非四个字母开头的字符串(例如:“123File”、"F1ile3"等)、大小写不同的相同单词(例如:"Window2010"和"winDows2010"等)、少于四个字母的字符串(例如:“aaa”、“dfs”等)、多个回车等;我的程序经过几次测试修改后,已经能够满足自己找到多个测试例子的要求,输出结果都是正确的,因此应该能够满足该程序的测试要求。
部分单元测试代码:
TEST_METHOD(TestMethod5)
{
    Core core;
    string txtName = "C:\\Users\\Hawk\\source\\repos\\WordCount\\WordCount\\Test\\test4.txt";
    string strTxt;
    int cnum=63, wnum = 6;
    strTxt = core.txtTostr(txtName);
    Assert::AreEqual(core.countCnum(strTxt), cnum);
    Assert::AreEqual(core.countWnum(strTxt), wnum);
}
TEST_METHOD(TestMethod6)
{
    Core core;
    string txtName = "C:\\Users\\Hawk\\source\\repos\\WordCount\\WordCount\\Test\\test5.txt";
    string strTxt;
    int cnum=9, wnum = 2;
    strTxt = core.txtTostr(txtName);
    Assert::AreEqual(core.countCnum(strTxt), cnum);
    Assert::AreEqual(core.countWnum(strTxt), wnum);
}
  • 在单元测试中我主要测试了文本的字符数函数和单词总数函数,使用Assert的AreEqual测试字符数和单词总数是否是正确的。
代码覆盖率:

1476119-20180911001814660-742603686.png

  • 代码覆盖率为84%。通过观察未覆盖的代码可知,未覆盖的部分是一些if...else中未执行的分支,可知代码覆盖率还是比较合理的。

异常处理:

输入异常:(无法打开输入文件)

1476119-20180911183106873-1375834540.png

输出异常:(无法打开输出文件)

1476119-20180911183118290-1873946204.png

心得与收获:

  • 通过本次编程作业,我才感受到这门《软件工程》课程并不是一门简单的编程实践课。如果只是简单的编程实践课,那么代码的编写便很随意了,没有接口封装的要求,只需要保证代码的可行性和正确性即可,甚至可以把代码全部放在main()函数里,但是那样的代码就显得很乱,可读性很差,所以一般我来写也会定义一些函数,将功能大致分开来写。一开始写代码的时候,我主要考虑的是代码最终功能的实现,在程序功能的分割方面做的不是很细致,这给我后面的封装和测试造成了很大的麻烦。
  • 因为我基本没有类似的项目经验,一些容器的使用等项目常用工具的掌握不是很熟练,需要临时的学习和使用,所以花费了较多的时间在搜索和阅读上。但是经过这样一番学习下来,对当前项目或有用的或没有用的都看了许多,我也确实学习到了很多东西,例如vector、unordered_map容器的简单使用等。经过了实践,我对之前学习的C++的知识又进行了一番检验,有了不一样的理解。
  • 对于这次编程作业,我感受得最深刻的是“规范”。拿到题目思考并有了解题思路后,并不是一股脑把代码写下去,而是要设计功能模块的划分,设计接口和函数,一步步实现代码。这样有规范地书写代码,不仅能保证代码的可读性,还能方便程序的测试和接口的封装。根据功能模块进行测试,你较快能够找到bug所在,但是如果是杂乱的代码的话,那么多次的测试出现bug可能会让你test疯掉。在进行算法优化时,只要找到相对应地模块所在便可以轻松修改代码,但如果代码的书写比较随意的话,需要修改的地方将不仅仅是一处。我感受到了不规范代码的痛苦,所以切身体会到了代码书写规范的好处,在今后的代码书写中,我将有意识地要求自己规范地书写代码,做到比较好的程度。

转载于:https://www.cnblogs.com/Jeho/p/9609311.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值