1、github地址html
https://github.com/hyt1022/wordcountjava
2、PSP表格git
PSP2.1github |
PSP阶段编程 |
预估耗时数组 (分钟)eclipse |
实际耗时ide (分钟)函数 |
Planning学习 |
计划 |
30 |
30 |
· Estimate |
· 估计这个任务须要多少时间 |
30 |
30 |
Development |
开发 |
840 |
1200 |
· Analysis |
· 需求分析 (包括学习新技术) |
60 |
60 |
· Design Spec |
· 生成设计文档 |
60 |
60 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
30 |
30 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
30 |
30 |
· Design |
· 具体设计 |
120 |
120 |
· Coding |
· 具体编码 |
360 |
600 |
· Code Review |
· 代码复审 |
60 |
60 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
120 |
240 |
Reporting |
报告 |
160 |
240 |
· Test Report |
· 测试报告 |
100 |
150 |
· Size Measurement |
· 计算工做量 |
30 |
30 |
· Postmortem & Process Improvement Plan |
· 过后总结, 并提出过程改进计划 |
30 |
60 |
|
合计 |
1030 |
1470 |
3、解题思路介绍
拿到题目后。首先对程序的准备进行了思考。
此次用Java进行编程,选用的编译器是Myeclipse10,配置的jdk版本是 。由于有一段时间没有写java,稍微有些生疏。所以查阅了大二时面向对象程序设计【1】。这本书对于java的基本语法和经常使用类进行了总结和用法指导,深刻浅出。
后面考虑到要生成exe文件,所以在网上查阅了如何将java项目打包生成exe文件【2】。
考虑到要将本地项目上传到github,在网上查阅了用命令行上传github的方法。【3】
再对程序的实现进行分析。
看到程序很容易就想到去年编译技术课程。此次的程序也能够沿用编译技术的部分的思想。
对于基础功能。最直接的想法是,经过将须要分析的* .c文件读入。用一个字符数组进行存储。对数组从到头尾扫一遍,在扫的过程当中,分析统计文件的字符,单词数,行数等。
对于拓展功能。在基础功能的基础上,在扫的过程当中考虑更多的因素。对于停用词表,现将词表读入文件,分红每个停用词,存入一个动态的字符数组。在扫待测数组时,将读到的每一个单词与停用词表进行比较,统计停用词的个数。统计代码行,空行,数据行的基本思路也和基础功能的一致。可是须要考虑更多的状态,以分析判断此行到底属于哪种类型。
对于基本输入输出。输入方式经过main函数自带的参数,分析args[],来判断输入的指令。输出则经过输入的文件名,将输出结果写入txt文件。
4、代码说明


public void analyse(String fileName,String stoplistFile,boolean f5) { Readfile(fileName); if(f5) { ReadStoplist(stoplistFile); } file = fileName; String tempword; String tep = new String(tempchars); int flag = 0;//判断上一个字符的标志,用来推断单词数是否增长;进入单词分隔符则为0,进入可显示字符则为1 while(tempchars[p] != '\0') { if(tempchars[p] == '\n') { flag = 0; l++; p++; } else if(tempchars[p] == '\r') { flag = 0; p++; } else if(tempchars[p] == '\t') { flag = 0; p++; } else if(tempchars[p] == ' ') { flag = 0; p++; } else if(tempchars[p] == ',') { flag = 0; p++; } else//若是是可显示的字符 { if(flag == 0)//若是上一个是单词分隔符则单词数加一 { w++; if(f5)//若是有停用词表则进行分析 { int a=p; while(tempchars[p] != '\n'&&tempchars[p] != '\r'&&tempchars[p] != '\t'&& tempchars[p] != ' '&& tempchars[p] != ','&&tempchars[p]!='\0') { p++; }//将这个单词的开始位置和结束位置作标记 tempword = tep.substring(a, p);//将这个单词截取下来 for(int i=0;i<stopword.size();i++)//将单词与停用词表进行对比 { if(tempword.equals(stopword.get(i))) { s++; } } p=p-1;//当前p指向单词间的分隔符,退回到此字符的前两个 } } flag = 1; p++; } } }
此段代码的主要逻辑是一个while循环,循环分析每个字符,以及当前对应的的状态。
读到每个字符则字符数加一。
读到每个换行符则行数加一,最后的行数还要加上第一行。
读到每个可显示字符,则判断前一个字符是否是单词分隔符,若是是则单词数加一;若是不是则前一个也是可显示字符,则为同一个单词,单词数不变。若是有停用词表,则将单词截取下来和词表中的单词进行对比。


public void analyseAddtion() { int i=0; int flag = 1; //此行是否分配 int cflag = 0; //此行是否处于注释里 int dflag = 0; // 此行是不是‘*/’的注释行 int count =0; //每行有效字符计数 while(tempchars[i] != '\0') { if(tempchars[i] == ' '||tempchars[i] == '\t'||tempchars[i] == '\r')//若是是空格一类的直接跳过 { i++; } else if(tempchars[i] == '/') { count++; if(tempchars[i+1] == '/') { if(flag == 1 && count<3 && cflag == 0)//此种状况可直接判断为注释行 { comment++; flag = 0; } } else if(tempchars[i+1] == '*') { cflag = 1; i++;//跳过一个字符,防止下一个是‘/’的状况 } i++; } else if(tempchars[i] == '*') { count++; if(tempchars[i+1] == '/') { if(cflag ==1) { cflag = 0; i++;//跳过一个字符,防止下一个是‘*’的状况 count = 0; dflag = 1; } } i++; } else if(tempchars[i] == '\n')//到每行末尾,若此行没有分配则根据当前状态进行分配 { if(flag == 1 && cflag == 0 && dflag == 0 && count<2) { empty++; flag = 0; } if(flag == 1 && cflag == 0 && dflag == 0 && count>1) { code++; flag = 0; } if(flag == 1 && cflag == 1) { comment++; flag = 0; } if(flag == 1 && cflag == 0 && dflag == 1) { comment++; flag = 0; dflag = 0; } count = 0; flag = 1; i++; } else//若是是其余字符 { count++; if(count > 1 && flag == 1 && cflag == 0)//此种状况可直接判断为代码行 { code++; flag = 0; } i++; } } //若最后一行没有分配,会经过当前状态进行分配 if(flag == 1 && dflag == 1) { comment++; } if(flag == 1 && cflag == 1) { comment++; } if(flag == 1 && cflag == 0 && dflag == 0 && count<2) { empty++; } if(flag == 1 && cflag == 0 && dflag == 0 && count>1) { code++; } }
此段代码的主要逻辑也是while循环,循环分析每个字符,以及当前对应的的状态。
若是读到空格,制表符和回车符,则直接跳过,这个对判断行没有影响。
若是读到’/’,则判断下一个是否是’/’,若是是且此行没有肯定是什么行,并且此行当前的可显示字符数不超过两个而且不在注释范围内 ,则可判断是注释行。若是下一个是’*’,则此行进入注释范围内(须要’*/’才能消除此注释范围)。
若是读到’*’,则判断下一个是否是’/’,若是是,判断是否在注释范围。若是在,退出注释范围,将当前可显示字符数清零,不在则当作两个可显示字符便可。
若是读到‘\n’,到了每行末尾,如此行还没肯定是什么行,则根据当前的状态判断,便是否在注释范围,是不是’*/’的注释行,是不是空行等。
最后在退出时,也会判断最后一的状态,肯定是什么行。


for(int i=0; i<args.length;i++)//循环判断每个字符串 { if(args[i].equals("-c")) { func[0] = true; if((i+1) == args.length)//若是此字符串为最后一个,则不符合格式,下同 { error = true; break; } if(args[i+1].charAt(0) != '-')//若是下一个不是'-'开头的指令,则默认为文件名,进行读取,下同 { i++; filename = args[i]; } } else if(args[i].equals("-w")) { func[1] = true; if((i+1) == args.length) { error = true; break; } if(args[i+1].charAt(0) != '-') { i++; filename = args[i]; } } else if(args[i].equals("-l")) { func[2] = true; if((i+1) == args.length) { error = true; break; } if(args[i+1].charAt(0) != '-') { i++; filename = args[i]; } } else if(args[i].equals("-o")) { func[3] = true; if((i+1) == args.length) { error = true; break; } i++; outputfile = args[i];//'-o'指令后必须接输出文件名,进行读取 } else if(args[i].equals("-e")) { func[4] = true; if((i+1) == args.length) { error = true; break; } i++; stoplistfile = args[i];//'-e'指令后必须接停用词表文件名,进行读取 } else if(args[i].equals("-a")) { func[5] = true; if((i+1) == args.length) { error = true; break; } if(args[i+1].charAt(0) != '-') { i++; filename = args[i]; } } else if(args[i].equals("-s")) { func[6] = true; if((i+1) == args.length) { error = true; break; } if(args[i+1].charAt(0) != '-') { i++; filename = args[i]; } } else { error = true; break; } }
循环判断,主函数自带的参数,是一个字符串数组。由此分析指令。’-c’, ’-w’, ’-l’, ’-a’指令后面若接文件名则是待分析的文件名,进行读取。‘-o’后接的文件名必然是输出文件名,’-e’后接的文件名必然是停用词表。分析后则可获得程序须要完成哪些功能,并接受到指令中相应的文件名。
5、测试设计过程
经过11个测试用例和13个执行语句来对程序进行测试。每一个测试用例均对于不一样的功能有所侧重,覆盖了系统能实现的绝大部分功能。根据白盒测试的思想,测试用例能也能覆盖绝大部分的条件判断语句。用例中出现换行和文件结尾时容易致使程序高风险,用例的字符数太高引发数组越界也会形成高风险。此测试方案基本能知足对此程序功能测试要求。如下会对每一个测试用例进行阐释和具体说明。
测试1:
用例:file1.c
用例说明:用于测试字符统计的基本功能,此用例包含了空格和制表符,测试可否正确统计空格即制表符。
执行语句:-c file1.c -o output1.txt
预计输出:file1.c,字符数:15
测试2:
用例:file2.c
用例说明::用于测试字符统计的基本功能,此用例包含空行,测试可否统计换行字符。
执行语句:-c file2.c -o output2.txt
预计输出:file2.c,字符数:22
测试3:
用例:file3.c
用例说明:用于测试单词统计的基本功能,此用例包含换行,空格,逗号隔开,测试可否正确统计单词数。
执行语句:-w file3.c -o output3.txt
预计输出:file3.c,单词数:16
测试4:
用例:file4.c
用例说明:用于测试停用词表功能,此用例测试基本功能,测试可否将例子中须要屏蔽的单词屏蔽。
执行语句:-w file4.c -e stoplist.txt -o output4.txt
预计输出:
file4.c,单词数:13
file4.c,停用后单词数:10
测试5:
用例:file5.c
用例说明:用于测试停用词表功能,此用例包含单词中含有停用词表的单词但不等于此单词,测试是否会误判断屏蔽。
执行语句:-w file5.c -e stoplist2.txt -o output5.txt
预计输出:
file5.c,单词数:20
file5.c,停用后单词数:20
测试6:
用例:file6.c
用例说明:用于测试行统计功能,此用例测试基础功能,包含空行,只有一个字符的行等。
执行语句:-l file6.c -o output6.txt
预计输出:file6.c,行数:9
测试7:
用例:file7.c
用例说明:用于测试代码行和注释行,此用例包含容易混淆的代码行和注释行的状况,测试可否正确统计行数。
执行语句:-a file7.c -o output7.txt
预计输出:file7.c,代码行/空行/注释行: 6/0/3
测试8:
用例:file8.c
用例说明:用于测试代码行和空行,此用例包含不少容易混淆的空行和注释行的状况,测试是否能正确统计行数。
执行语句:-a file8.c -o output8.txt
预计输出:file8.c,代码行/空行/注释行: 0/1/4
测试9:
用例:file9.c
用例说明:用于测试空行和代码行,此用例包含容易混淆的代码行和空行的状况,测试时候能正确统计行数。
执行语句:-a file9.c -o output9.txt
预计输出:file9.c,代码行/空行/注释行: 2/4/0
测试10:
用例:file10.c
用例说明:用于测试代码行,空行,注释行。此用例为综合用例,包含各类可能混淆的状况,测试可否正确统计行数。
执行语句:-a file10.c -o output10.txt
预计输出:file10.c,代码行/空行/注释行: 11/1/16
测试11:
用例:file1.c
用例说明:用来测试在不提供制定输出文件下,可否将结果输出在默认文件result.txt中。
执行语句:-l file1.c
预计输出:file1.c,字符数:15
测试12:
用例:当前文件夹中全部“.c”文件
用例说明:用于测试可否递归处理当前文件夹中全部符合条件的文件。
执行语句:-w -s .*.c -o output12.txt
预计输出:(较多不予以显示)
测试13:
用例:file11.c
用例说明:用于测试全功能,包含全部的功能指令,给出的用例中也包含了各类可能处理的状况,测试可否正常运行并给出正确的结果。
执行语句:-c -w -l -a -s *.c -e stoplist.txt -o output13.txt
预计输出:(较多不予以显示)
6、参考文献
【1】面向对象程序设计教程,任宏萍编著。
【2】https://www.cnblogs.com/yxwkf/p/4609765.html
【3】https://jingyan.baidu.com/article/0202781145eaab1bcc9ce5f0.html