Github项目地址
https://github.com/lsx337589597/wc
项目要求
wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。
实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。
基本功能列表
- -c [文件名] 返回文件的字符数(实现)
- -w [文件名] 返回文件词的数目(实现)
- -l [文件名] 返回文件的行数(实现)
扩展功能列表
- -s 递归处理目录下符合条件的文件。(实现)
- -a 返回更复杂的数据(代码行 / 空行 / 注释行)。(实现)
- .处理通配符(未实现)
高级功能列表
1. -x显示图形界面(未实现)
PSP开发耗时
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
20 |
30 |
· Estimate |
· 估计这个任务需要多少时间 |
120 |
300 |
Development |
开发 |
60 |
150 |
· Analysis |
· 需求分析 (包括学习新技术) |
60 |
70 |
· Design Spec |
· 生成设计文档 |
60 |
100 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
60 |
70 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
0 |
0 |
· Design |
· 具体设计 |
120 |
240 |
· Coding |
· 具体编码 |
120 |
240 |
· Code Review |
· 代码复审 |
20 |
40 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
40 |
30 |
Reporting |
报告 |
80 |
80 |
· Test Report |
· 测试报告 |
30 |
40 |
· Size Measurement |
· 计算工作量 |
10 |
10 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
20 |
50 |
合计 |
820 |
1450 |
解题思路
对于这项任务我的解题思路大概是用户输入命令,通过解析输入的命令来执行不同的操作,在主要的实现框架上应用io流和正则表达式。然后在开发过程中遇到各种各样细节上的难题就问同学或者看书找解答方法或者上网查询解答。
设计实现过程
代码分两个类,wcUtil类定义了实现基本功能的方法、算空行数的方法、算注释行的方法、算代码行的方法、递归处理目录下符合条件的文件;wcTest类定义了Test方法,用于在控制台输入输出,主方法用于执行Test方法
代码说明
实现基本要求的方法
public void basicFunction(File file) throws IOException{ int currentCharNum;//当前行字符数 int curStringLength=0;//当前行分割后各个字符串长度 int currentWordNum;//当前行单词数 BufferedReader br=new BufferedReader(new FileReader(file)); String currentLine=null; while((currentLine=br.readLine())!=null) { currentCharNum=0; currentWordNum=0; String tempCurLine = currentLine; //把当前行去掉前导和尾部空白后通过空格分割为字符串数组 String[] splitedString=currentLine.trim().split(" "); //计算该行中每个单独字符串的长度再相加,得到该行字符数 for(int i=0;i<splitedString.length;i++) { curStringLength=splitedString[i].length(); currentCharNum+=curStringLength; } //每行字符数相加得到总数 charNum+=currentCharNum; //统计单词数 //把当前行非字母的字符替换为空格 tempCurLine = tempCurLine.replaceAll("[^a-zA-Z]"," "); //以空格分割本行字符串为若干个字符串数组 String[] splitedString2=tempCurLine.trim().split("\\s+"); //字符串数组的长度即为该行单词数 currentWordNum=splitedString2.length; //避免当前行有字符且所有字符都不为字母时字符串数组无长度 if(currentWordNum!=0) { if("".equals(splitedString2[0])){ currentWordNum-=1; } } wordNum += currentWordNum; line++; } br.close(); }
计算空行
public void blankLine(File file) throws IOException { BufferedReader br=new BufferedReader(new FileReader(file)); String curLine=null; while((curLine=br.readLine())!=null) { if("".equals(curLine.trim())||"}".equals(curLine.trim())) {//去掉前后空白后字符串为空字符或者“}”判断为空行 blankLine++; } } br.close(); }
计算代码行
public void codeLine(File file)throws IOException{ BufferedReader br=new BufferedReader(new FileReader(file);
//利用正则表达式设计判断代码行的模式,去掉前后空白后以字母开头即为代码行 Pattern codeLinePattern=Pattern.compile("^[a-zA-Z].+"); String curLine=null; while((curLine=br.readLine())!=null) { Matcher m=codeLinePattern.matcher(curLine.trim()); if(m.matches()) { codeLine++; } } br.close(); }
计算注释行
public void commentLine(File file)throws IOException { BufferedReader br=new BufferedReader(new FileReader(file)); Pattern commentLinePattern=Pattern.compile(".*//.*"); Pattern commentPatternBegin=Pattern.compile("\\s*/\\*.*");//注释块头判断 Pattern commentPatternEnd=Pattern.compile(".*\\*/\\s*");//注释块尾部判断 String curLine=null; while((curLine=br.readLine())!=null) { Matcher m=commentLinePattern.matcher(curLine); if(m.matches()) commentLine++; if(commentPatternBegin.matcher(curLine).matches()) { commentLine++; do { curLine=br.readLine(); ++commentLine; }while(!commentPatternEnd.matcher(curLine).matches()); } } br.close(); }
递归处理目录
public void Recurve(File Directory) throws IOException { File[] files=Directory.listFiles(); if(files!=null&&files.length>0) { for(File f:files) { if(f.isDirectory()) { Recurve(f); }else { wcUtil w=new wcUtil(); w.basicFunction(f); w.blankLine(f); w.codeLine(f); w.commentLine(f); System.out.println("文件名为:"+f.getName()+" 的字符数:"+w.charNum+" "+"单词数:"+w.wordNum+" "+ "行数:"+w.line+" "+"空行:"+w.blankLine+" "+"代码行:"+w.codeLine+" "+"注释行:"+w.commentLine ); } } } }
wcTest方法
public void Test() throws IOException { Boolean s = true; while(s) { System.out.println("请输入命令:"); //输入命令 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String input=br.readLine(); if("-q".equals(input)) { s=false; System.out.println("已退出。。"); break; } String[] commond=input.split(" "); File file=new File(commond[1]); wcUtil wc=new wcUtil(); if(file.isFile()) {//传进直接文件 wc.basicFunction(file); wc.blankLine(file); wc.codeLine(file); wc.commentLine(file); switch(commond[0]) { case "-all": System.out.println("字符数:"+wc.charNum); System.out.println("单词数:"+wc.wordNum); System.out.println("行数:"+wc.line); System.out.println("空行:"+wc.blankLine+" "+"代码行:"+wc.codeLine+" "+"注释行:"+wc.commentLine); break; case "-c": System.out.println("字符数:"+wc.charNum); break; case "-w": System.out.println("单词数:"+wc.wordNum); break; case "-l": System.out.println("行数:"+wc.line); break; case "-a": System.out.println("空行:"+wc.blankLine+" "+"代码行:"+wc.codeLine+" "+"注释行:"+wc.commentLine); break; default: System.out.println("命令不正确"); break; } }else if(file.isDirectory()){//传进目录 switch(commond[0]) { case "-s-all": wc.Recurve(file); break; default: System.out.println("命令不正确!"); break; } } else { System.out.println("文件名错误"); } } }
测试运行
此为测试文本
运行结果
递归测试
代码覆盖率
项目小结
完成这次任务后,总的来说收获颇丰,但是还是存在很多的不足,首先,高级功能和处理通配符功能我没有做,主要是我没有能力在短时间内想出完成的方法因为我有别的事情干,再有就是我发现了我编程时候存在很多不良习惯而且有时候还容易把问题想复杂化,进而编写的程序也会看起来显得逻辑复杂,就例如在本次任务中对于基本功能我是合在一起用一个方法写完,导致该类的成员变量在方法之外,到了任务后期每次想要添加一些别的功能的时候,要创建一个新的类的对象,所以到调整代码的时候明明是想要修改一些小问题却牵扯到别的方面,非常麻烦,以后要改正这个习惯。不过这次任务我也学了东西,开始懂得使用正则,会用exe4j,基本了解了Github。