Github地址
https://github.com/ChenSilence/WordCount
PSP表格
PSP2.1 |
PSP阶段 |
预估耗时 (分钟) |
实际耗时 (分钟) |
Planning |
计划 |
15 |
15 |
· Estimate |
· 估计这个任务需要多少时间 |
30 |
15 |
Development |
开发 |
360 |
600 |
· Analysis |
· 需求分析 (包括学习新技术) |
60 |
60 |
· Design Spec |
· 生成设计文档 |
0 |
5 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
0 |
0 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
10 |
10 |
· Design |
· 具体设计 |
20 |
30 |
· Coding |
· 具体编码 |
240 |
420 |
· Code Review |
· 代码复审 |
40 |
40 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
60 |
100 |
Reporting |
报告 |
60 |
80 |
· Test Report |
· 测试报告 |
40 |
60 |
· Size Measurement |
· 计算工作量 |
10 |
20 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
10 |
20 |
|
合计 |
935 |
1475 |
解题思路
看到这个题目的时候,有点发怵,因为自己很久没有动手写Java代码了。并且看到需求说明里,要求按层次进行commit,因此我选择了先完成基础功能模块,即实现-w -l -c -o指令的解析,再逐步修改完成扩展功能模块即-s -a -e指令。
基本功能
对于计数单词以及行数(-c -w -l)这些基础功能来看,我刚开始是打算按字符类型逐个硬编码分析计数字符总数,单词总数,以及行数。虽然该方法可行,但是代码量必定很大,而且由于需要每个字符都要进行嵌套的判断,必然导致识别速度慢。如果源文件非常大的话,那么执行速度将是不可接受的。进而我果断放弃了该思路,去查询相应的可以使用的java函数,以期方便快速的实现这些统计功能。
对于-o心里大概有个数,只需要修改输出流路径,照这个思路去编码就可以了。
扩展功能
-s:递归查找后缀为.c的文件,将找到的所有路径保存到数组中,然后依次取出挨个执行命令。
-a:判断代码行,空行,注释行,同样由于效率的原因不得不排除了挨个判断字符的方法,在网上查询资料后了解到,可以使用正则表达式匹配的方法。因此又专门去学习了基本的正则表达式的写法,花费了很多的时间。
-e:停用词表。我的初步想法是在基本功能统计出单词的前提下,将输入文件与停用词表的单词逐个比对,计数匹配的数目,则总单词数目—匹配数目=考虑停用词后的单词数目。
程序设计实现过程
类的设计:
一共两个类,wordCount为主程序,testWC为测试类。
函数设计:
wordCount类中包含如下函数:
其中main函数无实际功能,只是生成实例调用entrance()函数。
然后再由entrance函数调用doPar进行参数解析,调用doOrder函数执行命令。
doOrder函数为代码的核心,根据参数处理结果,调用相应的子函数完成相应的功能。
testWC中只有一个test()函数,调用wordCount类中的entrance()函数对测试用例进行验证。
代码说明
首先展示解析args[ ]参数的函数doPar( ),
简单来说即只要包含有某命令(例如-w)时,就置相应的flag为1;若遇到特殊的如-e ,-o则将后面一个参数拷贝到对应的地址变量中。
1 public void doPar(String[] args) {//处理参数 2 3 for(int i = 0;i < args.length;i++) { 4 String nowPar = args[i]; 5 6 if (nowPar.length()==2) {//如果是命令 7 switch(nowPar.charAt(1)) { 8 case 'c':flag[0]=1;break; 9 case 'w':flag[1]=1;break; 10 case 'l':flag[2]=1;break; 11 case 'o': 12 flag[3]=1; 13 outFlag=true; 14 outFilePath =args[++i];//若识别出-o命令则将后一参数传给输出路径变量 15 break; 16 case 's': 17 flag[4]=1; 18 break; 19 case 'a': 20 flag[5]=1; 21 aFlag=true; 22 break; 23 case 'e': 24 flag[6]=1; 25 stopFlag=true; 26 stopFilePath =args[++i];//若识别出-e命令则将后一参数传给停用词表的路径变量 27 break; 28 29 default:break; 30 } 31 } 32 33 else sourceFilePath = nowPar;//当参数为路径而前面又无命令符说明时,判定为输入文件路径 34 } 35 }
下面是处理输出路径的实现,利用处理参数后置的outFlag值
1 if(!outFlag) {//不指定输出文件 2 fout = new FileWriter("bin/result.txt",true); 3 } 4 else //指定输出文件 5 fout = new FileWriter(outFilePath,true); 6 7 try { 8 fout.write(result.toString()); 9 } catch (IOException e) { 10 e.printStackTrace(); 11 } 12 fout.close();
统计字符函数
实现该函数时遇到的问题为,在windows环境下换行实际上“\r\n”两个字符,
而根据需求及所给例子来看,只应统计为一个,因此我采用了总字符减去\r字符数,代码如下:
1 public int cCharNumber() { 2 String text = readFile(sourceFilePath); 3 int num_r=0; 4 for(int k=0;k<text.length();k++) { 5 if (text.charAt(k)=='\r') { 6 num_r++; 7 } 8 } 9 return text.length()-num_r; 10 }
统计单词函数
本函数中,我将是否有停用词命令加入到了判定中,即在该函数体的不同分支来实现是否需减去停用词数。
其中本函数调用了doStop函数返回与停用词表相同的单词数目,下一并列出。
1 public int cWordNumber() { 2 String text = readFile(sourceFilePath); 3 String s[] = text.split("\\s+|,|\n");//split()里面是正则表达式 4 int wn = s.length; 5 if(stopFlag) { 6 return wn-doStop(s); 7 } 8 else 9 return s.length; 10 } 11 public int doStop(String[] s) {//与停用词表比对,统计出不该计算的单词数 12 String stoplist = readFile(stopFilePath); 13 String slist[] = stoplist.split(" +"); 14 int same=0; 15 for(int i=0;i<s.length;i++) { 16 for(int j=0;j<slist.length;j++) { 17 if (s[i].equals(slist[j])){ 18 same++; 19 } 20 } 21 } 22 return same; 23 }
统计行数功能简单,代码略。
统计不同类型的行数
如前所述,判断复杂,因此我采用的是使用正则表达式来匹配。
设计出了注释行,空行的正则表达式,剩下的为代码行,具体如下:
1 public String aCount() {//统计各行数 2 String all = null; 3 String text = readFile(sourceFilePath); 4 String s[] = text.split("\\r\\n"); 5 int noteline,spaceline,codeline; 6 noteline=spaceline=codeline=0; 7 for(String tmp:s) { 8 if(Pattern.matches("\\s*.?\\s*//.*|//.*", tmp))//注释行可能是x//或者//xxx的形式 9 noteline++; 10 else if(Pattern.matches("\\s*.?\\s*", tmp) || s==null)//空行可能有一个字符,或者本身为空串 11 spaceline++; 12 else 13 codeline++;//其他情况下为代码行 14 } 15 all=" 代码行/空行/注释行:"+codeline+"/"+spaceline+"/"+noteline; 16 return all; 17 }
递归处理
1 public static void find(String nowpath){ 2 File f=new File(nowpath+"\\");//首先获得当前路径 3 File nowf=new File(""); 4 File filelist[]=f.listFiles();//利用file的函数库中已有函数listfiles可方便的获得文件列表 5 if(filelist==null) {System.out.println("目录为空"); return;} 6 for(int i=0;i<filelist.length;i++) { 7 nowf = filelist[i]; 8 if (nowf.isFile() && nowf.getName().endsWith(".c")) { 9 // if(!source.contains(nowf.getAbsolutePath())) 10 source.addElement(nowf.getAbsolutePath());//循环判断后缀为.c文件的加入到路径集中 11 } else if (nowf.isDirectory()) { 12 find(nowf.getAbsolutePath());//递归 13 } 14 15 } 16 }
测试设计过程:
我首先考虑的是达到白盒测试的语句覆盖,即设计出的测试用例覆盖了所有语句。然后又针对条件分支进行了设计,
我自己私认为如下设计的12个测试用例可以满足要求。
1. "-c test4.c",
测试最基本的统计字符功能
2. "-w test4.c",
测试基本的统计单词功能
3. "-l test4.c",
测试行数统计
4."-c -w -l test4.c",
同时满足三个条件
5."-l -w test5.c -o result5.txt",
测试指定输出路径
6."-l -a test6.c -o result6.txt",
测试统计复杂的行数据,且统计所有行
7. "–a test4.c",
统计复杂的行数据
8. "–w test5.c –e stop.txt",
测试停用词表
9. "–w test6.c –e stop.txt -o result6.txt",
同时满足停用词表且指定输出路径
10."-w -l -s *.c",
测试递归
11. "-l -w -s D:\\java-oxygen\\eclipse-workspace\\WordCount\\*.c",
测试指定路径的递归
12 "-l -w -c -s -a -e stop.txt D:\\java-oxygen\\eclipse-workspace\\WordCount\\*.c -o result7.txt"
对最复杂的路径进行测试
参考文献链接
http://blog.csdn.net/chivalrousli/article/details/51146951 java 追加内容到文件末尾的几种常用方法
https://www.cnblogs.com/newcaoguo/p/6224071.html java中File类的getPath(),getAbsolutePath(),getCanonicalPath()区别
http://blog.csdn.net/centforever/article/details/47311891 Java Vector遍历的五种方法
https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 廖雪峰git教程
http://www.cnblogs.com/ningjing-zhiyuan/p/8563562.html 第2周个人作业:WordCount