本次大做业,咱们小组完成基本任务,扩展任务以及高级任务。下面是每一个任务的说明。git
一、首先给出GitHub地址:https://github.com/DM-Star/WordCount-optgithub
二、PSP表格性能优化
PSP2.1框架 |
PSP阶段编辑器 |
预估耗时ide (分钟)函数 |
实际耗时工具 (分钟)性能 |
Planning单元测试 |
计划 |
10 | 15 |
· Estimate |
· 估计这个任务须要多少时间 |
10 | 15 |
Development |
开发 |
700 | 800 |
· Analysis |
· 需求分析 (包括学习新技术) |
80 | 90 |
· Design Spec |
· 生成设计文档 |
30 | 50 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
60 | 80 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
20 | 30 |
· Design |
· 具体设计 |
50 | 50 |
· Coding |
· 具体编码 |
300 | 400 |
· Code Review |
· 代码复审 |
40 | 50 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
30 | 50 |
Reporting |
报告 |
100 | 80 |
· Test Report |
· 测试报告 |
50 | 40 |
· Size Measurement |
· 计算工做量 |
30 | 20 |
· Postmortem & Process Improvement Plan |
· 过后总结, 并提出过程改进计划 |
30 | 30 |
三、主要代码与接口说明
在这次大做业中,咱们组将整个任务分红4个模块,分别是核心模块、统计模块、状态模块和输入模块,和老师要求中说的有一点不一样,但咱们一块儿讨论以为咱们这个项目分红这样四个模块更加合理,我负责的是核心模块,核心模块主要是主函数和输出函数这两个部分(在这里也先提一下,就是这两个恰好用单元测试框架就很难实现,研究百度很久也不清楚怎么测main函数和代码中的output函数,因而没办法和其余模块同样用单元测试框架),下面给出部分代码和接口的解释。
①main函数主要代码
int main(int argc, char **argv) { fstream in; if (!inputCheck(argv[1], in)) { in.close(); return 0; } WordList wordList; wordCount(in, wordList); outPut("result.txt", wordList); in.close(); system("pause"); return 0; }
这个主函数里主要就是包含了你们写的不一样模块与接口,inputCheck是检查输入函数,WordList是为了记录单词及其词频的一个类,outPut就是输出函数,还有wordCount函数是对文件里面的单词进行词频统计和排序。首先用fstream in定义了一个文件对象(它封装了各类操做文件的方法),在接下来的函数中做为参数传入,在对文件输入判断检测后,对单词进行遍历统计,最后输出结果在result.txt文件上。
②outPut函数部分代码
void outPut(char outFile[], WordList &wordList) { ofstream outf(outFile); streambuf *default_buf = cout.rdbuf(); cout.rdbuf(outf.rdbuf()); wordList.outPut(); cout.rdbuf(default_buf); }
这是主运行函数中的输出代码,前三行输出重定向到文件,最后一行输出重定向到屏幕,中间一行负责用cout输出。
void WordList::outPut() { // 100 words are all output via cout Word *p = pWordHead->next; if (p != pWordTail) cout << p->word << ' ' << p->num; else return; p = p->next; for (int i = 0; i < 99; i++) { if (p != pWordTail) cout << endl << p->word << ' ' << p->num; else return; p = p->next; } }
这里是wordlist类中的一个输出函数,用于输出文件中的具体内容,根据其余组员设计的链表,这里的每一个节点p是一个结构体,里面定义的char word和int num,我这里用p->word和p->num表示。这段代码首先,定义一个指针指向从表头以后的第一个节点(这才是正文的内容开始),判断是不是表的尾部,若是不是,就输出内容,即单词和单词数;而后p指向下一个节点,全部的输出用一个for循环,根据老师要求,输出词频前100位的单词。
四、测试用例设计
在前面也提到了,在我提出main函数该怎么测试这个问题后,咱们你们都思考的状况下,都以为不知道怎么用框架去测试,后来组长也上去问了老师,老师的回答也是跟咱们一致的。后来我想,主函数和输出函数要测试的话,其实就是测试整个做业的输入输出了,因而我就设计了20个测试用例,至关于测试了咱们这整个做业的功能是否彻底完成,是否在遇到什么特殊状况是都能正确输出结果,以及这个项目的性能。
我设计测试用例的思想及整个过程以下:
首先对整个项目进行一个整体的测试,判断是否能正常输出想要的结果,以下图,与预期相同
而后老师任务要求中写只对txt文件进行分析,那对于其余格式的文件确定是没办法解析的,因此这里我选择的几种比较典型的文件格式试运行,例如.cpp文件、.h文件、word文件等,结果与预期同样,如图
而后就是测试的重点,功能测试,测试咱们的代码是否能彻底正确的运行想要的结果,在这,我设计的十几个测试用例,分别检测了特殊状况,如老师例子中给出的
有关单词识别的部分典型状况的说明:
第一,Let’s,这种包含单引号的状况,视为2个单词,即let和s。
第二,night-,带短横线的单词,视为1个单词,即night。
第三,“I,带双引号的单词,视为1个单词,即i。
第四,TABLE1-2,带数字的单词,视为1个单词,即table。
第五,(see Box 3–2).8885d_c01_016,带数字、经常使用字符和单词的状况,视为4个单词,即see, box, d, c。
前四种状况运行的蛮顺利,截图
在最后一种状况时,发现的一处问题,运行结果跟预期不一样,以下图,c单词明明只出现了一次,词频确实2。后来发现应该这个测试用例中“3–2”中的扩展字符,它都会读成单词c。
这个问题引起我又有了一个新的测试用例,即更明确验证一下是否是那个“–”引发的问题,因而我在上面的测试用例后面又加了好几个“–”,结果代表猜测是正确的,
而后我将以上测试放在同一个文件里,每种数量也翻倍,来进行综合测试,输出结果与预期相同。
而后还进行了对空文件的测试,若是文件为空,看会输出什么结果,运行如图
最后的测试就进行了一个简单的性能测试,特地制造了一个1M以上的大文件进行测试,经过对比原文件与不停复制粘贴后的大文件输出结果,发现结果无误,即也经过了小小的性能的测试。(具体的压力测试还在高级任务中)
综上,除了上面“–”引发的问题,其余全部测试用例输出都与预期相同,即完成了测试任务。
测试用例清单表以下图:
使用静态测试是为了确保代码符合行业规范。在这个项目中,咱们参考了Google给出的C++风格指南,而且对全部的代码进行了检查。Google给出的代码规范涉及的范围十分全面,从头文件、命名空间,一直到if……else……语句,到注释、空格、花括号,都给出了详尽的规范。咱们原本想使用Google提供的全部规范,但后来失败了。一方面,有些规范咱们不是很能理解,例如Google对于命名空间提出的规范,咱们并不能彻底地理解;另外一方面,规范中的某些内容咱们并不承认,好比对于变量的命名,Google推荐使用下划线分隔变量中的每一个单词,而咱们认为变量命名使用变量首字母小写、单词首字母大写,中间不使用空格分隔的方式也很好(并且这是面向对象程序设计老师推荐的命名方式)。
综上,最后咱们仅使用了头文件、注释、格式上的规范对整个项目进行代码检查。
进行代码检查,咱们使用的是一款也是由Google提供的代码检查工具cpplint(下载地址为https://github.com/google/styleguide/tree/gh-pages/cpplint)。这款工具十分方便,对于一款配置好Python环境的电脑来讲,只要将被测文件和脚本文件cpplint.py放在同一目录下,而后使用控制台运行Python脚本(被测文件路径做为参数),就可以快速地进行测试。
下面进行的第一次测试:
能够看出咱们的代码主要存在的问题就是格式,以及一点点头文件的问题。Google规定代码中全部用Tab制表符的地方都得使用空格,每一个花括号以前都要有一个空格,以及对于if……else……语句来讲,只要一个分支使用了花括号,那么全部的分支都要使用花括号,并且else分支必须和先后两个花括号处在同一行。对于注释,注释内容和双斜杠之间必需要留有一个空格(我也不知道为何)。
// 符合Google 规范的代码 if ((c >= 'a') && (c <= 'z')) { state = INNERWORD; } else if (c == '-') { if (state == INNERWORD) state = CRITICAL; else state = OUTERWORD; } else { state = OUTERWORD; }
对于头文件而言,每一个头文件都必须给出版权声明,还要进行#define保护。
// Copyright[2018]<Star> #ifndef WCPRO_WORDLIST_H_ #define WCPRO_WORDLIST_H_ // 在这里添加代码... #endif // WCPRO_WORDLIST_H_
不得不说,Google给出的规范真的是面面俱到。然而真的要实现它的全部规范我也不是很愿意。就拿#define保护来讲吧,这三行代码,在VS编译器下,只须要用 #pragma once 这一条指令就能够实现。对于if……else……分支的种种规范,我以为都不少余。我我的自己也有一套本身的格式规范,个人if语句看起来原本也至关顺眼,很是整齐,改为Google规范后反而看着不舒服了。就咱们所采用的那部分Google规范而言,咱们实际上都是差很少的。缘由就是,在VS编辑器中,调整代码格式能够很方便地使用一组组合键来完成(Ctrl+K 和 Ctrl+F),使用这个组合键,能够快速地将咱们的代码调整至VS的代码规范。对于没有采用Google规范的那部分(即,变量命名),我自认为胜他一筹,我对于变量命名,都会确实使用变量的含义命名,如pIndex, wordState等等,不会使用形如c, p, q 这样的简单的单字母来命名。
静态测试上面这部分的内容引自咱们组长的博客,由于这个静态检查是从我开始作的,而开始检查的时候,他也一块儿正旁边看,而后一块儿讨论一块儿改正,因此咱们想法一致,尤为是那个if……else……分支的种种规范,咱们改了好久,才改为了符合要求的规范。
由于开始检查的时候就是随便选了一个文件检查,截图的代码不是本身写的那部分,后面就没有截图,由于问题和改正方法都类似。
本次做业在个人mian函数和输出函数中性能优化中体现不大,可是在别的组员如输入模块上,对文件读取方面性能优化就比较须要,在读一个很大的文件时须要耗费的时间上就有体现。
本次大做业,相比我的做业,感受学习到的东西更多了,由于此次做业涉及到的知识面也多,学到了框架测试,静态测试,还有各类规范等等。第一次接触静态测试工具,跑出来结果的时候感受都惊呆了,居然几乎每行代码都是不符合规范的,因而一行一行的改,感受这个工具也是蛮神奇的。还有,一个团队做业时,不一样的同窗都有不一样的想法,互相交流讨论,感悟也不少。最后,要谢谢组长,带领咱们学习,圆满完成此次大做业!