WordCount

github:https://github.com/Hoyifei/SQ-T-Homework2-WordCount

PSP:

Planning

计划

   

· Estimate

· 估计这个任务需要多少时间

 10 minute  

Development

开发

   

· Analysis

· 需求分析 (包括学习新技术)

 5hour  About 1.5 hours(边看边写)

· Design Spec

· 生成设计文档

 0  0

· Design Review

· 设计复审 (和同事审核设计文档)

 0  0

· Coding Standard

· 代码规范 (为目前的开发制定合适的规范)

 0  0(已有)

· Design

· 具体设计

2hour  about 4hour 

· Coding

· 具体编码

 5hour  about 9 hour

· Code Review

· 代码复审

 0

· Test

· 测试(自我测试,修改代码,提交修改)

 5 hour  about 4 hour

Reporting

报告

 30 minurte  40 minute

· Test Report

· 测试报告

 0

· Size Measurement

· 计算工作量

 10 minute  about 10 minute

· Postmortem & Process Improvement Plan

· 事后总结, 并提出过程改进计划

10 minute    10 mminute
 

合计

   

 因为是零碎做的所以实际用时偏差很大,全部是估计值。只有值为0的非常确定

 

编程思路:

这有什么思路...挺直接的

处理argv,然后按要求统计就是

递归搜索:把通配符表达式转换成正则表达式,使用boost库的filesystem遍历,正则匹配一下

排除表其实有更好的解决方案,为排除表建立字典树,我偷懒直接逐个匹配了

进阶统计有几个细节,还有一个(可能是)UB(undefined Behavior):

/*

blahblah

*/*::这里不在注释里

/*

blahblah

*//::这里也不在

///*blahblah

::这里也不在注释里

/*UB:块注释里可以有空行,我的处理方法是既算注释也算空行

 

*/

argv里也有几个UB:

出现多个sourcefile:我的处理是全部接受进行统计

未指定统计操作:全部进行

此外,考虑到跨平台问题,-x参数没写弹对话框,而是从stdin中读取输入

 

测试思路:

最复杂的部分就是对于代码行,空行和注释的判定。为此我专门设计了test4.txt,重点测试了各种注释

其实我还是用了googletest,不过只是让测试结果的输出更好看一点,测试用的代码全都是自己写的

 

其他:

由于没有Windows系统 ,不仅编译出来的不是exe而是elf,而且测试在Windows下也跑不过(由于windows的路径使用反斜杠,会导致找不到输入,需要改一下),但是本体大概能跑(也没试过)。希望以后出作业能考虑一下跨平台的问题。

Git并非设计用来管理二进制文件,因为commit后没有机会删除,如果用来管理二进制文件会导致git目录迅速增长,占用过多空间。因此BIN文件夹下我会放一个txt文件,里面包含下载可执行文件(64位elf)的地址。依赖库也只能打包64位elf格式而且也不一定全。

只有类似于C/C++/Java的语法才会以//和/**/标识注释,所以出题时说清楚注释的判定标准比较好。严格来说因为没描述清楚所以注释的表示方法是个UB,我甚至可以写成统计Python/Shell的以#开头的行注释

输出全是英文所以格式不大一样

测试数据换行格式是Unix格式(\n)

下次做成Docker镜像就好了吧......

 

程序设计实现过程: 

总共一个类,test_basic只是把protected的成员暴露出来

主要流程:分析参数→如果存在Except文件则读取并登记词语→如果有搜索参数则进行搜索→依次处理所有统计并输出

统计字数:strlen

行数:至少一行,每一个\n多一行(这里没考虑mac的\r换行)

Vim等文本编辑器会在最后一行后面加一个\n,所以有看上去5行实际上有6行的问题。不过我认为这确实是多了一行所以没处理

单词:遇到非分隔符将其加到一个临时字符串的末尾,遇到分隔符时,如果临时字符串非空,则检查是不是出现在屏蔽列表里,如没有则单词数+1。不管是否被屏蔽清空临时字符串

递归搜索:具体流程已在解题思路中指出。

进阶统计:逐个读取。

如果为可见字符,本行可见字符数+1

如果为可见字符且并不位于注释中,本行代码字符+1

遇到/时判定上一个字符,如果不在注释里且上一个字符为/则为行注释开头(//),本行代码字符扣除//。如果位于块注释里且上一个字符为*则块注释结束且记当前字符为\0

遇到*时如果不在注释里则判定上一个字符,如果为/则为块注释开头,代码字符扣除/*

遇到\n时判定:代码字符大于一个为代码行,小于等于一个且包含注释则判定为注释行。如果可见字符数小于等于1则判定为空行。

代码解说:

进阶统计部分

 1 CWordCount::TAdvancedResult CWordCount::count_advanced() {
 2     TAdvancedResult ans;
 3     ans.empty=0;
 4     ans.code=0;
 5     ans.note=0;
 6     auto len=strlen(str);
 7     char c;
 8     int cur_line_code_chars=0,cur_line_visible_chars=0;
 9     bool line_note=false,block_note=false,line_include_note=false;
10     char last_char=0;
11     for(auto i=0;i<len;++i){
12         c=str[i];
13         switch(c){
14             case ' ':case '\t':case '\r':case '\0':{//空白符,忽略不计
15                 last_char=c;
16                 break;
17             }
18             case '\n':{//换行符,判定这一行的类型并清空
19                 if(cur_line_visible_chars<=1) ++ans.empty;
20                 if(cur_line_code_chars>1) ++ans.code;
21                 if(cur_line_code_chars<=1&&line_include_note) ++ans.note;
22                 cur_line_visible_chars=0;
23                 cur_line_code_chars=0;
24                 line_note=false;
25                 line_include_note=false;
26                 if(block_note) line_include_note=true;
27                 last_char=c;
28                 break;
29             }
30             case '/':{
31                 ++cur_line_visible_chars;
32                 if(!line_note&&!block_note) {//并非位于注释中,可以作为注释的开头
33                     if (last_char == '/') {
34                         --cur_line_code_chars;
35                         line_note = true;
36                         line_include_note=true;
37                         last_char=0;//如果作为块注释的结尾就不能再作为注释的开头了,“*//asdf”里asdf并不是注释
38                     }else {
39                         ++cur_line_code_chars;
40                         last_char=c;
41                     }
42                 }else if(block_note){//位于块注释中,可以作为注释的结尾
43                     if(last_char=='*'){
44                         block_note=false;
45                         last_char=0;
46                     }else{
47                         last_char=c;
48                     }
49                 }
50                 break;
51             }
52             case '*':{
53                 ++cur_line_visible_chars;
54                 if(!line_note&&!block_note) {//并非位于注释中,可以作为块注释的开头
55                     if (last_char == '/') {
56                         --cur_line_code_chars;
57                         block_note = true;
58                         line_include_note=true;
59                         last_char=0;//如果作为块注释的开头就不能作为块注释的结尾了,/*/asdf里asdf是注释
60                     }else {
61                         ++cur_line_code_chars;
62                         last_char=c;
63                     }
64                 }else{
65                     last_char=c;
66                 }
67                 break;
68             }
69             default: {
70                 if(!line_note&&!block_note) ++cur_line_code_chars;
71                 ++cur_line_visible_chars;
72                 break;
73             }
74         }
75     }
76     if(last_char!='\n') {//最后一行结尾可能没有换行,因此没有结算
77         if (cur_line_visible_chars <= 1) ++ans.empty;
78         if (cur_line_code_chars > 1) ++ans.code;
79         if (cur_line_code_chars <= 1 && line_include_note) ++ans.note;
80     }
81     //cerr<<ans.note<<" "<<ans.empty<<" "<<ans.code<<endl;
82     return ans;
83 }

 

测试思路:

代码中分支最多,最复杂, 处理情况最多,最容易出错的部分是处理程序参数和进阶统计部分。

对于其他的统计量没有特别的设计测试数据,而是在进阶统计部分的测试数据里顺便加上了对于其他统计的测试

 

递归搜索部分由于采用了相当成熟的库,因此只进行了简单的测试,也没有自动化地验证答案,而是人工进行了验证

 

由于程序接收参数的格式更大程度上取决于程序本身的规定,因此采用的是白盒测试

处理程序参数部分的代码采用了一个DFA模型,因此设计测试时覆盖了DFA的所有可能的状态转移,特别是覆盖了DFA的所有跳转到拒绝状态的路径。

 

由于对于代码行/空白行/注释行的判定有明确的标准,因此测试的方法是黑盒测试,通过判定程序能否正确处理输入并给出正确输出来进行测试

 

References:

Regular Expression:https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference

C++ regex:http://www.cplusplus.com/reference/regex/

Boost Filesystem:http://www.boost.org/doc/libs/1_66_0/libs/filesystem/doc/reference.html

posted @ 2018-03-19 17:24  Komeiji_Koishi  阅读(302)  评论(8编辑  收藏  举报