WordCount--统计输入文件的字符数、行数、单词数(java)--初级功能
码云地址:
https://gitee.com/YuRenDaZ/WordCount
个人PSP表格:
PSP2.1 |
PSP阶段 |
预估耗时 (分钟) |
实际耗时 (分钟) |
Planning |
计划 |
180 |
120 |
· Estimate |
· 估计这个任务需要多少时间 |
180 |
120 |
Development |
开发 |
580 |
440 |
· Analysis |
· 需求分析 (包括学习新技术) |
180 |
60 |
· Design Spec |
· 生成设计文档 |
40 |
30 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
20 |
20 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
20 |
10 |
· Design |
· 具体设计 |
20 |
20 |
· Coding |
· 具体编码 |
180 |
200 |
· Code Review |
· 代码复审 |
30 |
40 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
90 |
60 |
Reporting |
报告 |
90 |
70 |
· Test Report |
· 测试报告 |
40 |
30 |
· Size Measurement |
· 计算工作量 |
20 |
10 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
30 |
30 |
|
合计 |
850 |
630 |
一、功能需求分析
WordCount的需求可以概括为:对程序设计语言源文件统计字符数、单词数、行数,统计结果以指定格式输出到默认文件中,以及其他扩展功能,并能够快速地处理多个文件。可执行程序命名为:wc.exe,该程序处理用户需求的模式为:
wc.exe [parameter] [input_file_name]
存储统计结果的文件默认为result.txt,放在与wc.exe相同的目录下。
详细:
wc.exe -c file.c //返回文件 file.c 的字符数
wc.exe -w file.c //返回文件 file.c 的单词总数
wc.exe -l file.c //返回文件 file.c 的总行数
wc.exe -o outputFile.txt //将结果输出到指定文件outputFile.txt
注意:
空格,水平制表符,换行符,均算字符。
由空格或逗号分割开的都视为单词,且不做单词的有效性校验,例如:thi#,that视为用逗号隔开的2个单词。
-c, -w, -l参数可以共用同一个输入文件,形如:wc.exe –w –c file.c 。
-o 必须与文件名同时使用,且输出文件必须紧跟在-o参数后面,不允许单独使用-o参数。
二、程序设计
A:大概设计
1):程序由工具类和主函数类构成。
2):程序使用main的入口参数。
B:详细设计
1):将每一个“-?”指令对应一个方法。
2):将功能方法写在一个类里,作为工具类。
3):使用条件判断语句来处理命令。
4):用集合存储需要查询的文件,以方便进行添加和遍历。
5):程序主要逻辑在main函数中进行。(逻辑并不非十分复杂)
三、程序编码(编码并不是最好的实现方法,希望各位同学能指出不足之处,我们大家一起进步!)
- 主函数逻辑主要是由循环语句和条件判断语句完成。代码如下:
1 import java.util.HashSet; 2 import java.util.Set; 3 4 public class WordCount { 5 6 public static void main(String[] args) { 7 CountUtil countUtil = new CountUtil();//实例化工具类对象 8 Set<String> file_paths = new HashSet<String>() ; //创建用于存储输入文件的集合 9 String Output_file = "result.txt"; //存储输出文件名(默认为同目录下的result.txt文件) 10 String Result = ""; //存储查询结果 11 boolean isCharacters = false; //是否查询字符数 12 boolean isLines = false; //是否查询行数 13 boolean isWords = false; //是否查询单词数 14 for (int i = 0;i<args.length;i++) { //循环读取入口参数 15 if (args[i].startsWith("-")) { //判断是否是命令 16 switch (args[i]) { 17 case "-c": 18 isCharacters = true; //是-c指令,激活查询条件 19 break; 20 case "-l": 21 isLines = true; //是-l指令,激活查询条件 22 break; 23 case "-w": 24 isWords =true; //是-w指令,激活查询条件 25 break; 26 case "-o": 27 if(!args[i+1].startsWith("-")) //-o指令 判断后面是否有指定输出文件 28 { 29 Output_file = args[i+1]; //args[i+1]必须为输出文件 30 i++; 31 }else { 32 System.out.println("input error !"); //提示输入错误并终止程序 33 System.exit(0); 34 } 35 break; 36 default: 37 System.out.println("input error !"); 38 break; 39 } 40 } 41 else file_paths.add(args[i]); //将不属于命令的字符串存储在集合里面 42 } 43 44 if (isWords) { 45 Result+=countUtil.ReturnWords(file_paths)+"\r\n"; //调用查询单词方法,并做字符串拼接 46 } 47 if (isLines) { 48 Result+=countUtil.ReturnLines(file_paths)+"\r\n"; //调用查询行数方法,并做字符串拼接 49 } 50 if (isCharacters) { 51 Result+=countUtil.ReturnCharacters(file_paths)+"\r\n"; //调用查询字符数方法,并做字符串拼接 52 } 53 System.out.println(Result); 54 countUtil.OutputFile(Output_file, Result); //将结果输出到文件 55 } 56 }
- 在工具类中各方法的实现:(基本上都是基于IO流的操作)
ReturnCharacters方法,返回查询结果(String类型),参数为输入文件的集合。代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
ReturnWords方法,返回查询结果(String类型),参数为输入文件的集合。代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public String ReturnWords(Set<String> file_paths){ int Count = 0; //存储单词数 String result = "";//存储返回值 StringBuffer saveString = new StringBuffer();//因为string具有不可变性,用StringBuffer来进行读取的添加 String tmp = ""; //缓存字符串 FileInputStream in = null;//声明文件字符输入流 InputStreamReader isr = null;//声明字节输入流 BufferedReader bis = null;//声明缓存输入流 try { for (String file_path : file_paths) { //foreach循环遍历数组 in = new FileInputStream(file_path);//实例化文件输入流对象 isr = new InputStreamReader(in);//实例化字节输入流对象 bis = new BufferedReader(isr);//实例化缓存输入流对象 while ((tmp=bis.readLine())!=null) { //readLine()返回读取的字节,若没有了就返回null saveString.append(tmp); //将新读出来的数据接在已保存的后面 } tmp = saveString.toString(); //用字符串存储,以用split方法区分单词 String [] total = tmp.split("[\\s+,\\.\n]");//用正则表达式将字符串按单词格式分开 Count = total.length; //字符串数组长度就是单词个数 result += file_path+",单词数:"+Count+" "; //结果字符串拼接 Count = 0; } } catch (FileNotFoundException e) { System.out.println("有文件输入错误,请核对!(如果不会使用相对路径,请使用绝对路径)"); //检查到文件不存在,提示错误 System.exit(0); //结束程序 } catch (IOException e) { e.printStackTrace(); }finally { try { in.close();//关闭文件字符输入流 isr.close();//关闭字节输入流 bis.close();//关闭缓存输入流 } catch (IOException e) { e.printStackTrace(); } } return result; }
ReturnLines方法,返回查询结果(String类型),参数为输入文件的集合。代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
OutputFile方法,将结果保存在文件中,返回boolean型,参数为文件路径和内容。代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
四、代码测试
代码调试方法参照 https://www.cnblogs.com/xy-hong/p/7197725.html
由于是使用的main函数的入口参数,所以之前并未接触过这类方式的程序编写。在经过查阅后在改文章上找到了调试方式。具体操作再次不予详述,请参照以上链接。
Java代码的单元测试方法Java单元测试初体验(JUnit4)
单元测试代码:(我在测试的时候创建了包com.xzm.junit,里面专门存放单元测试的代码。由于是自带的单元测试,所以几乎可以完成“一条龙测试”)
1 package com.xzm.junit;
2
3 import static org.junit.Assert.*;
4
5 import java.util.HashSet;
6 import java.util.Set;
7
8 import com.xzm.wordcount.CountUtil;
9
10 public class Test {
11
12 @org.junit.Test
13 public void testReturnCharacters() {
14 Set<String> file_paths = new HashSet<String>();
15 String file1 = "F://test.c";
16 String file2 = "F://test.java";
17 file_paths.add(file2);
18 file_paths.add(file1);
19 System.out.println(new CountUtil().ReturnCharacters(file_paths));
20 }
21
22 @org.junit.Test
23 public void testReturnWords() {
24 Set<String> file_paths = new HashSet<String>();
25 String file1 = "F://test.c";
26 String file2 = "F://test.java";
27 file_paths.add(file2);
28 file_paths.add(file1);
29 System.out.println(new CountUtil().ReturnWords(file_paths));
30 }
31
32 @org.junit.Test
33 public void testReturnLines() {
34 Set<String> file_paths = new HashSet<String>();
35 String file1 = "F://test.c";
36 String file2 = "F://test.java";
37 file_paths.add(file2);
38 file_paths.add(file1);
39 System.out.println(new CountUtil().ReturnLines(file_paths));
40 }
41
42 @org.junit.Test
43 public void testOutputFile() {
44 String file_path = "result.txt";
45 String context = "OutPutFile test !";
46 new CountUtil().OutputFile(file_path, context);
47 }
48
49 }
点击右键→运行方式→JUnit测试,结果:
用例测试:
文件中的结果:
五、将项目导出为exe可执行文件
依赖工具:exe4j
参考方法:手把手教你如何把java代码,打包成jar文件以及转换为exe可执行文件(注意:改博客中没有指出32位和64位系统的差别,具体如下)
六、项目与远程库同步过程(码云)
有关git和码云项目的远程连接以及工作提交等相关操作参考Git和Github简单教程(该教程非常的详细,值得推荐。)
本项目一共有三次项目提交,分别是初始代码、复审代码、测试后修改的最终代码。
七、个人总结
第一次撰写博客有很多迷茫之处,但是在撰写博客的过程中我发现这其实是对整个项目过程的一次回顾与反思。在以前的作业中,通常都是写完代码测试完后就不再关注,也不会反在完成项目的途中犯过什么样的错误。每当下次遇到同样的问题还是会被困扰住,所以我觉得写博客是真的有必要的。虽然写出来的博客没人看,没人会在意,但是这并不是开始学习的我们的目的。我们的目的就在于回顾过程,反思过程中的错误,让项目的过程在我们脑海里留下更深的印象。最后,这次的作业的量还是算比较大的,但是却真的能感觉到有很多的收获。很多东西都是词不达意的,真的用心体会过就能明白。
感谢以上引用的各链接的作者们,希望你们的文章能帮助更多的人。