github地址:https://github.com/LiShengang/WCProject
一.PSP表格
PSP2.1 | PSP阶段 | 预估耗时 (分钟) | 实际耗时 (分钟) |
Planning | 计划 | 20 | 20 |
· Estimate | · 估计这个任务需要多少时间 | 20 | 20 |
Development | 开发 | 60 | 100 |
· Analysis | · 需求分析 (包括学习新技术) | 30 | 30 |
· Design Spec | · 生成设计文档 | 60 | 80 |
· Design Review | · 设计复审 (和同事审核设计文档) | 60 | 60 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 60 | 100 |
· Design | · 具体设计 | 100 | 150 |
· Coding | · 具体编码 | 500 | 1200 |
· Code Review | · 代码复审 | 60 | 60 |
· Test | · 测试(自我测试,修改代码,提交修改) | 40 | 60 |
Reporting | 报告 | 60 | 120 |
· Test Report | · 测试报告 | 60 | 60 |
· Size Measurement | · 计算工作量 | 30 | 30 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 40 |
合计 | 1190 | 2130 |
二.解题思路
按照作业要求先做基本功能,再做拓展功能。基本功能的实现思路较为简单,用main(String [] args)接受命令行传参,接收到参数后,用for循环
将参数逐一取出来,然后用switch判断是否为'-c'或'-w'或'l',从而决定输出哪项内容。统计字符,单词,行数的原理为:单独写一个函数
wcount(InputStreamReader isr)接收从控制台传入的文件名如:test.c。在wcount中用isr.read()一个一个读取字符,读一个字符字符数加1;当前一个
字符是,或空格或换行或Tab键且后一个字符不是它们其中任意一个时,单词计数加1;当遇到换行符时,行计数加1。 '-o'指定输出文件功能的实现
原理:FileOutputStream auto =new FileOutputStream(new File(args[flag2])); PrintStream pri =new PrintStream(auto);pri.println(“***********”);
该语句功能是向指定文件args[flag2]中输入特定内容,其中args[flag2]是从命令行输入的参数,flag2是命令行输入的参数的序号,即输入的第几个参数。
做完基本功能后,我开始入手难度系数较大的拓展功能,我先做的是停用词表的功能,思路很简单,在基本功能的基础上,将源文件中的单词全部统计
出来,再把停用词表中的单词 全部统计出来,逐一进行比对,如果有相同的,源文件在统计单词数量时减1。接下来我实现的功能是自动寻找本目录及
子目录下指定后缀的所有文件,首先我从网上查阅了有关资料,找到了获取本工作路径的方法:File dir= new File(".");另写了一个函数
getAllFile(File paramFile,String s,String ss) ,用paramFile.isFile()判断是否为文件,如果不是文件那就是目录。如果是文件就判断后缀是否为所需类型
文件,如果是全部统计下来;如果是目录就用paramFile.listFiles()统计目录下所有的文件,将所有符合要求的文件全部统计下来。
三.程序设计实现过程
程序所有功能在一个类(class WordCountCla)中实现,将各项功能划分为许多模块,每个模块用一个函数来实现。main(String [] args)是入口函数,
同时接收从命令行输入的参数;wcount(InputStreamReader isr)是计算字符数,单词数,行数的函数;stop(InputStreamReader isr)是存储停用表内单词
的函数;clear()为清空字符数,单词数,行数的函数;getAllFile(File paramFile,String s,String ss)为递归获取目录及子目录下文件的函数;程序由这些
函数拼接而成,main函数调用其他函数实现特定的功能。
四.代码说明
1.main函数:从命令行获取相应的参数如"-c","-w","-l","-a","-s","-o","-e",并在运行时处理各项参数。
由于main函数过长,不在这里显示,想查看main函数请参考github链接里Bin/WCfunction/下的所有功能源代码,里面有完整的代码
2.wcount函数:统计字符,单词,行数,此函数为整个程序的支柱,所有有关数目的计算都要用到这个函数
public static void wcount(InputStreamReader isr) throws IOException { int c = 0; int empty=0; int charnum=0; int noempty=0; String results = ""; boolean lastWhite = true; String whiteSpace = " \t\n\r,"; while((c = isr.read()) != -1) { //统计字符数 if(c!='\r') { chars++; } //统计行数 if(c == '\n') { lines++; if(noempty==0)emptyline++;else noempty=0; } //统计单词数 int index = whiteSpace.indexOf(c); if(index == -1) { charnum++;/************************/ if(charnum>=2) noempty=1;/*大于一个字符时不是空行*/ if(lastWhite == true) { words++; lastWhite = false; } results += (char)c; } else { lastWhite = true; // char[] results1 = results.toCharArray(); result[sum1]=results;//出现单词分隔符时将之前的单词统计在result[]数组中 results =""; sum1++; } } if(c==-1){result[sum1]=results;results ="";sum1++;} if(chars != 0) lines++; }
3.stop函数:统计停用词表里的单词,以便于与源文件中的单词作比较,如果有与源文件中相同的单词,则在统计源文件单词数目时自动减1
public static void stop(InputStreamReader isr) throws IOException { int c = 0; String results = ""; boolean lastWhite = true; String whiteSpace = " \t\n\r,"; while((c = isr.read()) != -1) { int index = whiteSpace.indexOf(c); if(index == -1) { if(lastWhite == true) { lastWhite = false; } results += (char)c; } else { lastWhite = true; stoparray[sum2]=results;//出现单词分隔符时将之前的单词统计在result[]数组中 results =""; sum2++; } } if(c==-1){stoparray[sum2]=results;results ="";sum2++;}//最后一个单词后面没有分隔符,但也要统计上 }
4.clear()函数:清空已统计的数目
public static void clear() { chars = 0; words = 0; lines = 0; }
5.getAllFile函数:获取exe所在目录及子目录下指定格式的所有文件
public static void getAllFile(File paramFile,String s,String ss) { if (paramFile.isFile()) {// 是文件,则添加到文件列表中,本次调用结束 if(ss!="") { if(paramFile.getName().endsWith(ss)) { clear(); filearray[filenum]= paramFile.getName(); filenum++; FileReader Re; try{ Re = new FileReader(s+"/"+paramFile.getName()); wcount(Re); worarray[tip]=words; chaarray[tip]=chars; linarray[tip]=lines; emptylinearray[tip]=emptyline; for(int n=0;n<sum1;n++) { finalarray[tip][n]=result[n]; } finalresult[tip]=sum1; sum1=0; tip++; } catch(IOException e) { return; } } } else //不输入*.c时,默认为.c文件 { if(paramFile.getName().endsWith(".c")) { clear(); filearray[filenum]= paramFile.getName(); filenum++; FileReader Re; try{ Re = new FileReader(s+"/"+paramFile.getName()); wcount(Re); worarray[tip]=words; tip++; } catch(IOException e) { return; } } } } else {// 是目录 File[] localFiles = paramFile.listFiles();// 得到目录下子文件数组 if (localFiles != null) {// 目录不为空 for (File localFile : localFiles) {//遍历子文件数组 getAllFile( localFile,paramFile.getName(),ss);//调用该函数本身 } } // 为空目录,本次调用结束 } }
五.测试设计过程
我编写了18个测试用例,放在了测试用例.txt文件里,基本上覆盖了程序所要求的所有功能,在命令行上输入测试用例,一步一步验证功能,
测试的分支要逐渐细分,尽量不要遗漏。测试用例举例:1.wc.exe -c -w -l -a -s *.c -o output.txt -e stopList.txt 2.wc.exe -c -w -l -a testfile.c -o output.txt -e stopList.txt
另外我编写了测试脚本文件testscript.exe(已上传github),在测试脚本文件中调用了wc.exe并给其传参,运行脚本文件可直接输出结果,不用再从命令行上一步一步输入。
例:单词数20,总行数14,空行6,注释行4,代码行4
void main() { //注释行 int a,b,x,y; /* 注释行 */ if (a == b) x = y; else x = -y; }
以下是部分测试用例截图
wc.exe -c -w -l testfile.c
wc.exe -c -w -l test.c -o output.txt -e stopList.txt
wc.exe -c -w -l -s *.c
wc.exe -c -w -l -s *.c -o output.txt -e stopList.txt
测试脚本testscript.exe运行界面截图
六.参考链接
1.http://blog.csdn.net/ycy0706/article/details/45457311
2.https://zhidao.baidu.com/question/193429854
3.http://blog.csdn.net/zgljl2012/article/details/47267609