github项目地址
https://github.com/liyizhu/wc.exe
WC 项目要求
- 基本功能列表:
wc.exe -c file.c //返回文件 file.c 的字符数(实现)
wc.exe -w file.c //返回文件 file.c 的词的数目 (实现)
wc.exe -l file.c //返回文件 file.c 的行数(实现)
- 扩展功能:
-s 递归处理目录下符合条件的文件。(实现)
-a 返回更复杂的数据(代码行 / 空行 / 注释行)。(实现)
[file_name]: 文件或目录名,可以处理一般通配符。(实现)
空行:本行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”。
代码行:本行包括多于一个字符的代码。
注释行:本行不是代码行,并且本行包括注释。
- 高级功能:
-x 参数。这个参数单独使用。如果命令行有这个参数,则程序会显示图形界面,用户可以通过界面选取单个文件,
程序就会显示文件的字符数、行数等全部统计信息。(未实现)
PSP表格
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
20 |
20 |
· Estimate |
· 估计这个任务需要多少时间 |
10 |
10 |
Development |
开发 |
500 |
420 |
· Analysis |
· 需求分析 (包括学习新技术) |
150 |
120 |
· Design Spec |
· 生成设计文档 |
30 |
40 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
20 |
30 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
10 |
5 |
· Design |
· 具体设计 |
30 |
10 |
· Coding |
· 具体编码 |
200 |
300 |
· Code Review |
· 代码复审 |
30 |
30 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
60 |
90 |
Reporting |
报告 |
120 |
120 |
· Test Report |
· 测试报告 |
40 |
80 |
· Size Measurement |
· 计算工作量 |
10 |
10 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
30 |
30 |
合计 |
|
1250 |
1315 |
解题思路描述
功能:wc.exe -c file.c //返回文件 file.c 的字符数
思路:逐行读取文件,去掉每行空字符后,统计个数,累加,直到文件读取完毕
功能:wc.exe -w file.c //返回文件 file.c 的词的数目
思路:逐行读取文件,将非字母替换为空格,然后按空格切割字符串为字符串数组,累加字符串数组长度,直到文件读取完毕
功能:wc.exe -l file.c //返回文件 file.c 的行数
思路:逐行读取文件,没读取一行,行数加一,知道文件读取完毕
功能:-a 返回更复杂的数据(代码行 / 空行 / 注释行)。
思路:逐行读取文件,判断是否存在//,/*,*/若存在,判定为注释行行数加一,判断是否只存在一个字符,或者为空,若是,判定为空行,若判断行中存在/*,则将标志域设置为true,直到读取到*/,将标志域设置为false,当标志域为true,判定为注释行,若非以上情况,判定为代码行
功能:[file_name]: 文件或目录名,可以处理一般通配符。
思路:将通配符*替换为\\.+表示多个任意字符,将通配符?替换为\\.表示一个任意字符,将文件名与该正则表达式匹配,若匹配,执行相应操作
功能:-s 递归处理目录下符合条件的文件。
思路:列举目录下的所有文件,若为目录,则递归调用函数,若为文件且符合文件格式,则执行相应操作,否则不作任何操作
设计实现过程
说明:
一共有两个类:WcUtil是工具类,WcMain类中只有main方法,用于处理控制台输入的参数,解析,判断是否合法,
并调用WcUtil类中相应的方法
代码说明
main() 功能:循环处理命令行参数,解析,判断参数的合法性,调用相应的处理方法
1 public class WcMain { 2 /* 3 * 主方法 4 */ 5 public static void main(String[] args) { 6 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 7 //当前命令 8 String currentCommand = null; 9 //当前命令切分后的数组 10 String[] currentCommandStrings = null; 11 while(true) { 12 try { 13 //读取命令 14 if((currentCommand=br.readLine())!=null) { 15 currentCommandStrings = currentCommand.split(" "); 16 //命令字符串数组长度小于二或者长度大于四,为错误命令 17 if(currentCommandStrings.length<2||currentCommandStrings.length>4) { 18 //空命令直接跳过 19 if(currentCommandStrings.length==1&&"".equals(currentCommandStrings[0])) { 20 21 }else { 22 System.out.println("错误命令"); 23 System.out.println("输入\"wc.exe help\"或\"wc.exe ?\"查看帮助文档"); 24 } 25 26 }else { 27 //命令第一个字段必须为wc.exe 28 if("wc.exe".equals(currentCommandStrings[0])){ 29 //查看具体命令 30 if(currentCommandStrings.length==3) { 31 switch (currentCommandStrings[1]) { 32 case "-c": 33 WcUtil.charNumber(currentCommandStrings[2]); 34 break; 35 case "-w": 36 WcUtil.wordNumber(currentCommandStrings[2]); 37 break; 38 case "-l": 39 WcUtil.lineNumber(currentCommandStrings[2]); 40 break; 41 case "-a": 42 WcUtil.complexData(currentCommandStrings[2]); 43 break; 44 45 default: 46 System.out.println("错误命令"); 47 System.out.println("输入\"wc.exe help\"或\"wc.exe ?\"查看帮助文档"); 48 break; 49 } 50 }else if(currentCommandStrings.length==2) { 51 switch (currentCommandStrings[1]) { 52 case "help": 53 case "?": 54 WcUtil.showHelpDoc(); 55 break; 56 default: 57 System.out.println("错误命令"); 58 System.out.println("输入\"wc.exe help\"或\"wc.exe ?\"查看帮助文档"); 59 break; 60 } 61 }else if(currentCommandStrings.length==4&&"-s".equals(currentCommandStrings[1])) { 62 String currentDirectory = currentCommandStrings[2]; 63 //路径是否存在 64 if(new File(currentDirectory).isDirectory()) { 65 String wildcardChar = currentCommandStrings[3].replaceAll("\\*","\\.+").replaceAll("\\?","\\."); 66 //System.out.println(wildcardChar); 67 WcUtil.recurseHandleAllFiles(currentDirectory,wildcardChar); 68 System.out.println("遍历结束"); 69 }else { 70 System.out.println("错误:找不到指定路径"); 71 } 72 }else { 73 System.out.println("错误命令"); 74 System.out.println("输入\"wc.exe help\"或\"wc.exe ?\"查看帮助文档"); 75 } 76 }else { 77 System.out.println("错误命令"); 78 System.out.println("输入\"wc.exe help\"或\"wc.exe ?\"查看帮助文档"); 79 } 80 } 81 }else { 82 System.out.println("错误命令"); 83 System.out.println("输入\"wc.exe help\"或\"wc.exe ?\"查看帮助文档"); 84 } 85 86 } catch (IOException e) { 87 e.printStackTrace(); 88 } 89 } 90 91 } 92 93 }
lineNumber() 功能:计算文件行数
1 public static void lineNumber(String fileName) { 2 //判断文件是否存在且是否为c文件 3 if(new File(fileName).isFile()&&!fileName.endsWith(".c")) { 4 System.out.println("不支持该文件类型"); 5 return; 6 } 7 int lineNumber = 0; 8 try { 9 BufferedReader br = new BufferedReader(new FileReader(fileName)); 10 while((br.readLine())!=null) { 11 lineNumber++; 12 } 13 br.close(); 14 System.out.println("文件:"+fileName+" 行数:"+lineNumber); 15 }catch (FileNotFoundException e1) { 16 System.out.println("错误:文件不存在"); 17 return; 18 //e1.printStackTrace(); 19 }catch(IOException e2) { 20 System.out.println("错误:文件读取出错"); 21 //e2.printStackTrace(); 22 } 23 24 }
charNumber() 功能:计算文件字符数
1 public static void charNumber(String fileName) { 2 if(new File(fileName).isFile()&&!fileName.endsWith(".c")) { 3 System.out.println("不支持该文件类型"); 4 return; 5 } 6 int charNumber = 0; 7 try { 8 BufferedReader br = new BufferedReader(new FileReader(fileName)); 9 String currentLine = null; 10 String[] currentLineStrings = null; 11 while((currentLine=br.readLine())!=null) { 12 currentLineStrings = currentLine.trim().split("\\s+"); 13 for(int i=0;i<currentLineStrings.length;i++) { 14 charNumber = charNumber+currentLineStrings[i].length(); 15 //System.out.println(currentLineStrings[i]+currentLineStrings[i].length()); 16 } 17 } 18 br.close(); 19 System.out.println("文件:"+fileName+" 字符数数:"+charNumber); 20 } catch (FileNotFoundException e1) { 21 System.out.println("错误:文件不存在"); 22 return; 23 //e1.printStackTrace(); 24 }catch(IOException e2) { 25 System.out.println("错误:文件读取出错"); 26 //e2.printStackTrace(); 27 } 28 29 }
wordNumber() 功能:计算文件单词数
1 public static void wordNumber(String fileName) { 2 if(new File(fileName).isFile()&&!fileName.endsWith(".c")) { 3 System.out.println("不支持该文件类型"); 4 return; 5 } 6 //单词数 7 int wordNumber = 0; 8 try { 9 BufferedReader br = new BufferedReader(new FileReader(fileName)); 10 //当前行字符串 11 String currentLine = null; 12 //当前行切分后的数组 13 String[] currentLineStrings = null; 14 15 Pattern p = Pattern.compile("[_a-zA-Z0-9]+"); 16 17 Matcher m = null; 18 while((currentLine=br.readLine())!=null) { 19 //将当前字符串中非字母下划线的字符全部替换为空格 20 currentLine = currentLine.replaceAll("[^_a-zA-Z]"," "); 21 currentLineStrings = currentLine.trim().split("\\s+"); 22 for(int i=0;i<currentLineStrings.length;i++) { 23 m = p.matcher(currentLineStrings[i]); 24 if(m.matches()) { 25 wordNumber++; 26 } 27 //System.out.println(currentLineStrings[i]); 28 } 29 } 30 br.close(); 31 System.out.println("文件:"+fileName+" 单词数:"+wordNumber); 32 } catch (FileNotFoundException e1) { 33 System.out.println("错误:文件不存在"); 34 return; 35 //e1.printStackTrace(); 36 }catch(IOException e2) { 37 System.out.println("错误:文件读取出错"); 38 //e2.printStackTrace(); 39 } 40 41 }
complexData() 功能:计算文件更复杂的数据(代码行,空行,注释行)
1 public static void complexData(String fileName) { 2 if(new File(fileName).isFile()&&!fileName.endsWith(".c")) { 3 System.out.println("不支持该文件类型"); 4 return; 5 } 6 //代码行 7 int codeLineNumber = 0; 8 //空行 9 int blankLineNumber = 0; 10 //注释行 11 int annotationLine = 0; 12 //记录多行注释是否结束 13 boolean annotationBegin = false; 14 try { 15 BufferedReader br = new BufferedReader(new FileReader(fileName)); 16 String currentLine = null; 17 while((currentLine=br.readLine())!=null) { 18 if(annotationBegin) { 19 annotationLine++; 20 //多行注释结束 21 if(currentLine.contains("*/")) { 22 annotationBegin = false; 23 } 24 //多行注释开始 25 }else if(currentLine.contains("/*")){ 26 annotationLine++; 27 annotationBegin = true; 28 }else if(currentLine.contains("//")) { 29 annotationLine++; 30 }else if(currentLine.trim().length()>1) { 31 codeLineNumber++; 32 }else { 33 blankLineNumber++; 34 } 35 36 } 37 br.close(); 38 System.out.print("文件:"+fileName+" "); 39 System.out.println("代码行:"+codeLineNumber+" 空行:"+blankLineNumber+" 注释行:"+annotationLine); 40 }catch (FileNotFoundException e1) { 41 System.out.println("错误:文件不存在"); 42 return; 43 //e1.printStackTrace(); 44 }catch(IOException e2) { 45 System.out.println("错误:文件读取出错"); 46 //e2.printStackTrace(); 47 } 48 49 }
recurseHandleAllFiles() 功能:递归处理目录下的所有文件
1 public static void recurseHandleAllFiles(String directory,String wildcardChar) { 2 Pattern p = Pattern.compile(wildcardChar); 3 Matcher m = null; 4 File file = new File(directory); 5 if(file.isDirectory()) { 6 if(file.listFiles()!=null) { 7 for(File item:file.listFiles()) { 8 if(item.isDirectory()) { 9 recurseHandleAllFiles(directory+"\\"+item.getName(),wildcardChar); 10 }else { 11 m = p.matcher(item.getName()); 12 if(m.matches()&&item.getName().endsWith(".c")) { 13 complexData(directory+"\\"+item.getName()); 14 } 15 } 16 } 17 } 18 } 19 }
showHelpDoc() 功能:显示帮助文档
1 public static void showHelpDoc() { 2 System.out.println("本程序仅支持c程序文件,即.c文件"); 3 System.out.println("wc.exe -c file.c 返回文件 file.c 的字符数"); 4 System.out.println("wc.exe -w file.c 返回文件 file.c 的单词数"); 5 System.out.println("wc.exe -l file.c 返回文件 file.c 的行数"); 6 System.out.println("wc.exe -a file.c 返回文件 file.c 的代码行数、空行数、注释行数"); 7 System.out.println("wc.exe -s directory *.c (支持通配符,*代表多个任意字符,?代表一个任意字符) 返回directory路径下所有符合条件的文件的代码行数、
空行数、注释行数"); 8 System.out.println("wc.exe -x 显示图形界面,选取单个文件,显示文件的字符数、行数等全部统计信息"); 9 }
测试运行
运行测试
单元测试
代码覆盖率
项目小结
1、首次使用软件工程的方法,虽然前期花费的时间稍多,但是后面编码思路更加清晰,改善过程不需要大幅度修改代码
2、首次使用JUnit进行单元测试,回归测试,单元测试覆盖率检测,认识到单元测试的重要性
3、重新学习了git和github的使用,更加熟悉git的各种操作
4、学习了exe4j的使用