一、Github项目地址
https://github.com/pollydeer/code
二、需求分析
实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。
程序处理用户需求的模式为:
wc.exe [parameter] [file_name]
- 功能列表:
- 基本功能:(已实现)
wc.exe -c file.c //返回文件 file.c 的字符数
wc.exe -w file.c //返回文件 file.c 的词的数目
wc.exe -l file.c //返回文件 file.c 的行数
2. 扩展功能:
wc.exe -s //递归处理目录下符合条件的文件。
wc.exe -a //返回更复杂的数据(代码行 / 空行 / 注释行)
3. 高级功能:
wc.exe -x //图形界面
三、解题思路
在刚拿到这个题目的时候,其实是有点懵的。因为之前在java里对文件的读写部分知识了解没有很深,所以当时对如何把输入的信息作为参数传入这个点感觉到很困惑。后来我就先查阅了一些资料,了解了如何输入参数这个问题。
随后,在看到题目的基本功能时,我认为返回行数是在字符数、词数、行数中最简单的、最容易入手的,然后字符数的难度是其次,难度比较高的是词的数目。而且我发现这是一个循序渐进的过程,三个功能之间也有互相借鉴的地方。所以我是从行数开始入手这个项目的。比较重要的是要结合正则表达式对符合条件的字符、单词等进行过滤、筛查。
由于之前对java的认识只停留在理论层面,真正实践的次数寥寥可数。因此在我决定用java开始,我就开始重新翻看书的相关类的使用、相关方法的使用,也有在网上找一些书上一笔带过的方法,或者根本没细讲的类。
四、设计实现过程
-
基本功能
我将三个基本功能:统计行数、字符数、词数都封装在了一个Count类里,三个方法分别是CountLine(String filename)、CountWord(String filename)、CountCharacter(String filename)。三者是并列关系。还有一个Main类,里面是main方法,用来调用Count类里的三个方法。除此之外,为了进行单元测试,我建了一个test类包,里面含CountTest类,用来对三个方法进行单元测试。
1. 统计行的数目
2. 统计单词的数目
3. 统计字符的数目
五、代码说明
-
基本功能
1.统计行的数目
基本思路是:利用BufferedReader类的readline()方法,实现对行的计数。
1 public static int CountLine(String filename) { 2 int LineNum = 0; 3 FileReader file = null; //尚未分配内存空间 4 BufferedReader brin = null; //尚未分配内存空间 5 try { 6 file = new FileReader(filename); 7 brin = new BufferedReader(file); 8 while((brin.readLine()) != null) { //循环读取分行文本,判断是否已经读到文件末尾 9 LineNum++; //行数+1 10 } 11 System.out.println("文件的行数为:" + LineNum); 12 brin.close(); //关闭流 13 file.close(); 14 }catch (FileNotFoundException e) { 15 System.out.println(filename + "无法在该路径找到文件"); 16 return -1; 17 }catch (IOException e) { 18 System.out.println(filename + "输入的文件名有误"); 19 return -1; 20 } 21 return LineNum; 22 }
2.统计单词的个数(注:此处不把数字作为单词计算)
基本思路是:用正则表达式将非英文字符转化为空格,再根据空格进行分割,这样即可实现分割成一个个词语的功能。
1 public static int CountWord(String filename) { 2 String s; 3 String[] s3 = null; 4 int WordNum = 0; 5 FileReader file = null; 6 BufferedReader brin = null; 7 try { 8 file = new FileReader(filename); 9 brin = new BufferedReader(file); 10 Pattern p = Pattern.compile("[_a-zA-Z]+"); //指定正则表达式的规则为至少匹配一个下划线、大小写英文字母 11 Matcher m = null; 12 while((s = brin.readLine()) != null) { //分行文本非空时,将分行文本插入到sb中 13 s = s.replaceAll("[^_a-zA-Z]"," "); //此处认为数字不属于单词,且文件中的单词可含下划线 14 s3 = s.trim().split("\\s+"); //先把两端的空格去掉,再对中间内容根据空格进行分割 15 for(int i = 0; i<s3.length;i++) { 16 m = p.matcher(s3[i]); //把s3的每个元素都返回Matcher实例 17 if(m.matches()) { //验证s3[i]中的每个元素是否与Pattern.compile的正则表达式模式一致 18 WordNum++; //一致则单词数++ 19 } 20 } 21 } 22 System.out.println("文件的单词数为:" + WordNum); 23 brin.close(); //关闭流 24 file.close(); //关闭文件流 25 }catch (FileNotFoundException e) { 26 System.out.println(filename + "无法在该路径找到文件"); 27 return -1; 28 }catch (IOException e) { 29 System.out.println(filename + "输入的文件名有误"); 30 return -1; 31 }//由于PatternSyntaxException为运行时刻异常,此处不进行处理 32 return WordNum; 33 }
3.统计字符的个数
基本思路是:分行读取文本,将读出的内容存入一个字符数组中,当字符非空格时,统计字符数目的变量自增1。
1 public static int CountCharacter(String filename) { 2 String s; 3 int CharNum = 0; 4 char[] ch; 5 FileReader file = null; 6 BufferedReader brin = null; 7 try { 8 file = new FileReader(filename); 9 brin = new BufferedReader(file); 10 while((s = brin.readLine()) != null) { //分行读取文本,结果为string类型 11 ch = s.toCharArray(); //将一行读取出来的内容存入字符数组中 12 for(char temp : ch) { //遍历ch数组,如果发现有空格则不对CharNum进行++操作 13 if(temp != ‘ ‘) { 14 CharNum++; 15 } 16 } 17 } 18 System.out.println("文件的字符数为:" + CharNum); 19 brin.close(); //关闭流 20 file.close(); 21 }catch (FileNotFoundException e) { 22 System.out.println(filename + "无法在该路径找到文件"); 23 return -1; 24 }catch (IOException e) { 25 System.out.println(filename + "输入的文件名有误"); 26 return -1; 27 } 28 return CharNum; 29 }
4.Main函数
1 public static void main(String[] args) throws IOException,ArrayIndexOutOfBoundsException{ 2 int i; 3 String temp = null; 4 System.out.println("请输入命令:"); 5 Scanner input = new Scanner(System.in); //从cmd输入控制参数及file_name 6 if(input.hasNext()) { 7 temp = input.nextLine(); //结束扫描的输入 8 } 9 String[] strwords = temp.split(" "); //用空格分隔输入的参数以及文件名 10 String filename = strwords[strwords.length-1]; //路径为输入的最后一个参数 11 12 13 for(i = 0; i<strwords.length-1; i++) //从开头到文件名字前面都是属于parameter 14 { 15 if(strwords[i].equals("-c")) 16 Count.CountCharacter(filename); 17 if(strwords[i].equals("-w")) 18 Count.CountWord(filename); 19 if(strwords[i].equals("-l")) 20 Count.CountLine(filename); 21 } 22 input.close(); //关闭流 23 }
六、测试运行
-
基本功能
各文件的内容截图:
测试运行的截图:
1.空文件(test1.c)
2.只有单个字符(test2.c)
3.只有单行(test3.c)
4.完整源代码(test4.c)
★单元测试及代码覆盖率测试(注:此处仅以统计字符个数方法的截图为例,其他方法以相同方法进行测试)
-
单元测试
1.代码
package test; import static org.junit.Assert.*; //import org.junit.Before; import org.junit.Test; import src.Count; import org.junit.Ignore; public class CountTest { @Ignore public void setUp() throws Exception { } @Test public void testCountChar() { assertEquals(6,Count.CountCharacter("C:\\Users\\Dell\\Desktop\\test100.txt")); } @Test public void testCountWord() { assertEquals(2,Count.CountWord("C:\\Users\\Dell\\Desktop\\test\\test3.txt")); } @Test public void testCountLine() { assertEquals(1,Count.CountLine("C:\\Users\\Dell\\Desktop\\test\\test3.txt")); } }
2.截图(以统计字符个数方法的单元测试为例进行截图)
上图左侧的绿色表示测试通过且无误。
上图的红色矩形表示测试有部分失败,可以看到,正确的文件字符数为8,而我在testCountChar方法中,输入的文件字符期望值是6,两数不一致,因此测试失败。
上图也是出现了测试失败的问题。原因是我手动把文件的路径设成了一个不存在的文件“test100.txt”,且在设计CountChar方法时,我把FileNotFoundException异常设计成了方法的返回值为-1。而此处在测试时,我输入的期望字符数是6,两数不一致,从而测试失败。
3. 代码覆盖率
上图为程序正常运行时的代码覆盖率截图。
上图为程序出现异常时的代码覆盖率截图。
上图为单元测试时的代码覆盖率截图。
七、PSP
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
30 |
40 |
· Estimate |
· 估计这个任务需要多少时间 |
30 |
40 |
Development |
开发 |
370 |
476 |
· Analysis |
· 需求分析 (包括学习新技术) |
40 |
90 |
· Design Spec |
· 生成设计文档 |
10 |
15 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
10 |
10 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
10 |
8 |
· Design |
· 具体设计 |
60 |
48 |
· Coding |
· 具体编码 |
200 |
175 |
· Code Review |
· 代码复审 |
30 |
40 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
60 |
90 |
Reporting |
报告 |
100 |
100 |
· Test Report |
· 测试报告 |
30 |
60 |
· Size Measurement |
· 计算工作量 |
30 |
20 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
40 |
30 |
合计 |
550 |
616 |
八、项目小结
总得来说,这是一次对自己很好的一个锻炼机会。因为之前学java的时候都是简单停留在理论上,可以说是知识还在书里,并没有被吸收、内化。这次决定用java来写个人项目真的是把理论变为实践的一次很好的尝试,期间磕磕碰碰也遇到了很多问题。归根结底还是熟悉度的问题。
因为我是初次尝试用eclipse进行单元测试和代码覆盖率的测试,期间摸索如何进行单元测试也耗费了比较多的时间,实际编码的时间其实相对来说并不算很长。但是我在这个过程中也发现对于main方法如何书写是比较陌生的,特别是要传入参数的时候比较束手无策。其次,在这个过程中,我也辨别了StringBuffer类、BufferedReader类、StringReader类等等很容易混淆的类,也熟悉了一些,当然还需要后期再继续使用熟悉一下。除此之外,在这个过程中我也发现每个类是有很多很多的方法可以使用的,在实际应用时必须得弄清作用才能让逻辑正确。
这次因为初次尝试用java写,的确熟悉度是一个很大的限制因素。在这次个人项目中,我实现了基本功能,也把过程都尽量完整记录。但是可惜的是扩展功能和高级功能都未能实现,可以在之后继续把剩下的任务钻研、完成,多把在这个过程中得到的经验都汲取、吸收、内化了。
原文地址:https://www.cnblogs.com/pollydeer/p/12563367.html