WordCount--实现字符,单词,代码统计
Github:
https://github.com/whoNamedCody/WordCount
PSP表格
PSP2.1 |
PSP阶段 |
预估耗时 (分钟) |
实际耗时 (分钟) |
Planning |
计划 |
10 | 15 |
· Estimate |
· 估计这个任务需要多少时间 |
20 | 20 |
Development |
开发 |
180 | 250 |
· Analysis |
· 需求分析 (包括学习新技术) |
10 | 30 |
· Design Spec |
· 生成设计文档 |
10 | 20 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
20 | 20 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
10 | 20 |
· Design |
· 具体设计 |
20 | 20 |
· Coding |
· 具体编码 |
100 | 150 |
· Code Review |
· 代码复审 |
20 | 30 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
50 | 100 |
Reporting |
报告 |
20 | 40 |
· Test Report |
· 测试报告 |
30 | 40 |
· Size Measurement |
· 计算工作量 |
10 | 10 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
5 | 15 |
合计 |
515 | 780 |
需求说明:
WordCount的需求可以概括为:对程序设计语言源文件统计字符数、单词数、行数,统计结果以指定格式输出到默认文件中,以及其他扩展功能,并能够快速地处理多个文件。
1、基础功能:
-c:输出字符,
-l:输出代码行,
-w:输出单词,
-o:输出
2、 扩展功能:
-a:输出代码行/空行/注释行,
-s:递归处理文件,
-e:停用词表,
解题思路:
1、java实现,但需要转成exe在cmd下输入命令执行,所以流程是:编码(.java)-->Export(.jar)-->exe4j(.exe)
2、java里有static void main(String[] args),里面的args就是cmd下的命令数组,比如输入-c -l -w file.c(空格隔开),那么args数组长度为4
3、对args数组进行遍历分析,找出输入命令和文件名,比如-c -l -w -a 后面跟的是输入文件名,-s后面会跟*.c之类的通配文件,-e后面会跟停用词表stoplist.txt,-o接输出文件result.txt
这里识别出命令和文件后,会再对文件分析,比如-s ./WordCount/*.c,需要截取出文件的上一层(相对)路径./WordCount和后缀.c,方便后续递归文件夹下的.c文件
4、将所有的统计封装成一个函数:public void command(String[] args,String inputFile,String outputFile,WordCount WC)
5、创建一个WordCount类,里面的属性有字符数,单词数,行数,代码行,空行,注释行,如果没有出现-s:则只实例化一个对象WordCount,调用一次command函数,输出前面的结果,如果出 现 -s:即递归处理文件夹下面的*.c文件(当然*.txt,*.doc文件也是可以的),就声明多个WordCount对象,循环对对象属性进行赋值。如果需要输出就调用get和set方法获取属性值。
6、递归处理文件:根据相对路径递归寻找文件夹下的后缀文件。
程序设计实现:
类:
public class WordCount{}
函数方法设计:
WordCount(int,int,int,int,int,int)是构造函数;get和set是MyEclipse自动生成的的getter和setter方法,main(string [ ])是程序入口,分析判断指令格式;command(String [ ],String,String,WordCount)执行指令,返回相应指令的统计值;wc(String,String)对输入文件进行统计;inStop(String,String[ ])判断单词是否在停用词表内;getFile(File)递归获取文件。
代码说明:
1、执行命令:需要输出的有-c,-w,-l,-a,判断指令,直接暴力循环,优化日后再说。
1 //命令执行,根据命令输出数据到屏幕和outputFile中 2 public void command(String[] args,String inputFile,String outputFile,WordCount WC)throws IOException{ 3 String outResult=""; 4 inputFile=inputFile.substring(inputFile.lastIndexOf("\\")+1, inputFile.length()); 5 for(int i=0;i<args.length;i++){ 6 if(args[i].equals("-c")) 7 outResult=outResult+inputFile+",字符数:" + WC.getCharCount()+"\r\n"; 8 } 9 for(int i=0;i<args.length;i++){ 10 if(args[i].equals("-w")) 11 outResult=outResult+inputFile+",单词数:" + WC.getWordCount()+"\r\n"; 12 } 13 for(int i=0;i<args.length;i++){ 14 if(args[i].equals("-l")) 15 outResult=outResult+inputFile+",行数:" + WC.getLineCount()+"\r\n"; 16 } 17 for(int i=0;i<args.length;i++){ 18 if(args[i].equals("-a")) 19 outResult=outResult+inputFile+",代码行/空行/注释行:"+WC.getCodeCount()+"," 20 +WC.getSpaceCount()+","+WC.getNoteCount()+"\r\n"; 21 } 22 //写数据到outputFile 23 System.out.println(outResult); 24 File writename = new File(outputFile); 25 writename.createNewFile(); 26 BufferedWriter out = new BufferedWriter(new FileWriter(writename,true)); 27 out.write(outResult); 28 out.flush(); 29 out.close(); 30 }
2、统计功能字符数、单词数、行数、代码行/空行/注释行
提一下代码行/空行/注释行
1 //统计功能字符数、单词数、行数、代码行/空行/注释行 2 public void wc(String inputFile,String stopFile) throws IOException{ 3 String lineString = null; 4 String[] buffer=null; //文件每行 5 String[] buffer1 = null;//stoplist 6 7 boolean isNote = false; 8 int notNote=0; 9 10 //读取停用词表 11 if(useStop){ 12 File dirr=new File(stopFile); 13 BufferedReader bff = new BufferedReader(new FileReader(dirr)); 14 while((lineString=bff.readLine())!=null){ 15 buffer1=lineString.split(",| "); 16 } 17 bff.close(); 18 } 19 lineString = null; 20 21 // 读取输入文件inputFile 22 File dir=new File(inputFile); 23 BufferedReader bf = new BufferedReader(new FileReader(dir)); 24 while((lineString=bf.readLine())!=null){ 25 26 //遇到 , 空格 就结束赋值 27 buffer=lineString.split(",| "); 28 for(int i=0;i<buffer.length;i++){ 29 30 //使用停用词表则剔除词表内单词,不用则不踢 31 if(useStop){ 32 if(!buffer[i].equals("")&&!inStop(buffer[i], buffer1)){ 33 wordCount++; 34 } 35 } 36 else{ 37 wordCount++; 38 } 39 40 } 41 if(buffer.length!=1) 42 lineCount++; 43 44 charCount+=(lineString.length()+1); 45 46 47 lineString=lineString.trim(); 48 //空行,一个字符的也算空行 49 if (lineString.matches("^[//s&&[^//n]]*$")||lineString.length()==1) { 50 spaceCount++; 51 } 52 //注释/*的开始 53 else if (lineString.startsWith("/*") && !lineString.endsWith("*/")||((lineString.startsWith("{/*") 54 ||lineString.startsWith("}/*"))&&!lineString.endsWith("*/"))){ 55 noteCount++; 56 isNote=true; 57 } 58 //没有遇到*/ 59 else if(isNote&&!lineString.endsWith("*/")&&!lineString.startsWith("*/")) { 60 notNote++; 61 noteCount++; 62 } 63 //遇到*/ 64 else if (isNote == true && (lineString.endsWith("*/")||lineString.startsWith("*/"))) { 65 noteCount++; 66 isNote=false; 67 } 68 //注释行 69 else if (lineString.startsWith("//")|| lineString.startsWith("}//")||lineString.startsWith("{//")|| 70 ((lineString.startsWith("{/*") ||lineString.startsWith("}/*")||lineString.startsWith("/*")) 71 && lineString.endsWith("*/"))) { 72 noteCount++; 73 } 74 else{ 75 codeCount++; 76 } 77 } 78 bf.close(); 79 noteCount-=notNote; 80 codeCount+=notNote; 81 }
1 //判断是否在停用词表内 2 public static boolean inStop(String str,String[] buffer){ 3 int count=0; 4 for(int i=0;i<buffer.length;i++){ 5 if(str.equals(buffer[i])){ 6 count++; 7 } 8 } 9 if(count>0) 10 return true; 11 else 12 return false; 13 }
4、遍历文件目录,如-s ./test/*.c则递归遍历./test目录下的.c文件
1 //遍历目录文件 2 public static List<File> getFile(File dir) { 3 List<File> files = new ArrayList<File>(); 4 // 此文件下的所有文件和文件夹集合 5 File[] subs = dir.listFiles(); 6 for (File file : subs) { 7 if (file.isFile() && file.getName().endsWith(endStr)) { 8 // 把获取到的后缀文件添加到集合中,可以是任何后缀文件 9 files.add(file); 10 } else if (file.isDirectory()) 11 //如果是目录,就进行递归 12 files.addAll(getFile(file)); 13 } 14 return files; 15 }
测试设计过程:
设计测试用例的思路是采用白盒测试的语句覆盖,尽可能把测试用例覆盖所有可能的程序语句,当然特别是分支语句,会多做测试,如-s的是否递归处理文件在我的程序中是一个分支语句
基本功能:
1、测试-c:输出file.c字符数
wc.exe -c file.c
2、测试-c、-w、-l:输出file.c字符数,单词数、行数 (当前根目录)
wc.exe -c -w -l file.c
3、测试-w 、-l 、-c:输出./testFile/file.c 字符数,单词数、行数 (根目录下其他路径,命令顺序)
wc.exe -w -l -c ./testFile/file.c
4、测试-c 、-l 、-w、-o:输出file.c字符数,单词数、行数、输出到文件resultAdd.txt
wc.exe -c -l -w file.c -o resultAdd.txt
扩展功能:
5、测试-a:输出file.c的代码行/空行/注释行
wc.exe -a file.c
6、测试-s、-a:输出根目录下所有.c文件的代码行/空行/注释行
wc.exe -s -a *.c
7、测试-e:测试停用词表stoplist.txt
wc.exe -w file.c -e stoplist.txt
8、测试-c、-w、-l 、-a、-o:输出file.c字符数,单词数、行数 、代码行/空行/注释行输出到result.txt
wc.exe -c -w -l -a file.c -o result.txt
9、测试-a、-s、-e:测试递归,代码行/空行/注释行,停用词表stoplist.txt
wc.exe -a -s ./testFile/*.c -e stoplist.txt
10、测试-c、-w、-l、-a、-e、-o:对不递归进行测试
wc.exe -c -w -l -a file.c -e stoplist.txt -o result.txt
11、测试-c、-w、-l、-a、-s、-e、-o:覆盖所有的指令测试
测试脚本
点击testScript.exe可以执行完11个测试用例,类似于批处理的方式,但只有一个进程,批处理测试用例。
下面是java代码,简单叙述一下思路:创建一个进程打开之前的wc.exe,逐行读取测试用例testCase.txt,一行是一条测试用例。
1 package test; 2 import java.io.BufferedInputStream; 3 import java.io.BufferedReader; 4 import java.io.File; 5 import java.io.FileReader; 6 import java.io.IOException; 7 import java.io.InputStreamReader; 8 import java.util.Scanner; 9 10 public class TestScript { 11 //进程p 12 static Process p; 13 public static void main(String[] args) throws IOException{ 14 String lineString=null; 15 File dirr=new File("testCase.txt"); //测试用例文件 16 if(dirr.exists()){ 17 BufferedReader bff = new BufferedReader(new FileReader(dirr)); 18 while((lineString=bff.readLine())!=null){ 19 //非空行执行测试用例 20 if(!lineString.trim().matches("^[//s&&[^//n]]*$")){ 21 System.out.println(lineString); 22 command(lineString); 23 } 24 } 25 bff.close(); 26 }else{ 27 System.out.println("没有该测试文件"); 28 } 29 30 Scanner sc = new Scanner(System.in); 31 String scc=sc.nextLine(); 32 } 33 34 public static void command(String cmd){ 35 Runtime run = Runtime.getRuntime();//返回与当前 Java 应用程序相关的运行时对象 36 try { 37 p = run.exec(cmd);// 启动另一个进程来执行命令 38 BufferedInputStream in = new BufferedInputStream(p.getInputStream()); 39 BufferedReader inBr = new BufferedReader(new InputStreamReader(in)); 40 String lineStr; 41 while ((lineStr = inBr.readLine()) != null) 42 System.out.println(lineStr); 43 if (p.waitFor() != 0) { 44 if (p.exitValue() == 1) 45 System.err.println("命令执行失败!"); 46 } 47 inBr.close(); 48 in.close(); 49 } catch (Exception e) { 50 e.printStackTrace(); 51 } 52 } 53 54 }
参考链接: