一、Github项目地址:https://github.com/aier02/wordcount

二、PSP表格

PSP2.1

PSP阶段

预计耗时

(分钟)

实际耗时

(分钟)

Planning

计划

 20

 15

· Estimate

· 估计这个任务需要多少时间

 20

 15

Development

开发

 365

 690

· Analysis

· 需求分析 (包括学习新技术)

 120

 150

· Design Spec

· 生成设计文档

 40

 30

· Design Review

· 设计复审 (和同事审核设计文档)

 15

 20

· Coding Standard

· 代码规范 (为目前的开发制定合适的规范)

 20

 10

· Design

· 具体设计

 30

 60

· Coding

· 具体编码

 120

 350

· Code Review

· 代码复审

 30

 30

· Test

· 测试(自我测试,修改代码,提交修改)

 90

 40

Reporting

报告

 65

 90

· Test Report

· 测试报告

 30

 20

· Size Measurement

· 计算工作量

 15

 10

· Postmortem & Process Improvement Plan

· 事后总结, 并提出过程改进计划

 20

 60

三、解题思路:

  1)认真阅读武老师给出的任务书,分析基本需求,并将老师文字描述的关键部分复制粘贴到本地文件,根据自己的理解写出需求文档。

  2)根据需求分析技术关键点:用户的命令行输入,参数的检测,文件获取和读写,文件内容的扫描,这些功能显然都有相通的关键点:字符串匹配,根据以往的经验,解决此类问题的关键是利用正则表达式。

  3)根据老师推荐的java语言,查找java正则表达式的相关函数和用法,参考网址:http://www.runoob.com/java/java-regular-expressions.html;java的字符串分割方法,参考网址:https://www.cnblogs.com/xiaoxiaohui2015/p/5838674.html

  4)文件名的确定和读写,参考网址:http://mouselearnjava.iteye.com/blog/1959690;读写文件的方式,参考网址:https://www.cnblogs.com/1175429393wljblog/p/5918150.html

四、程序实现的过程

  1)根据java的代码组织,wordcount.java中包括了程序的主逻辑和入口函数,WC.java根据命令行的参数进行相关的操作,readFileByLines.java是对文件进行逐行读写,fileFinder.java是递归查找目录和子目录下所有的指定后缀的文件。

  2)类和函数

类名

主要函数

作用

wordcount

main()

逻辑判断,传递参数给WC类,写入文件

WC

public WC(String theCommand)

构造函数,根据命令行参数对相应的属性赋值并调用其他的count函数

 

count*(String theFile)

Count类型的函数,根据正则表达式的结构统计各种数目

readFileByLines

public ArrayList<String> fileString()

逐行读取指定文件名的文件内容,并保存为字符串数组

fileFinder

findFiles(String filenameSuffix, String currentDirUsed, 

            List<String> currentFilenameList)

根据文件后缀,文件目录递归寻找符合条件的文件,并保存在第三个参数中(字符串数组)

五、关键代码说明

  1)入口函数:

 1 //利用正则表达式检查用户命令行输入参数是否正确
 2          String pattern = "wc.exe (-[cwlsa] )+[^(-[cwlsa] )]+(\\.)[^ ]+( -e .+(\\.)txt)?( -o .+(\\.)txt)?";
 3          boolean isMatch = Pattern.matches(pattern, com);
 4          //表达式正确则跳出循环,否则继续接受用户输入
 5          if(isMatch){
 6              //根据命令行参数,新建WC类,根据WC的flag数组的对应数字是否为1,判断要往文件中逐行写入哪些内容
 7              WC wordCount=new WC(com);
 8              if(wordCount.flag[0]==1){
 9                  System.out.println(wordCount.getFile()+" "+"字符数:"+" "+wordCount.getNumofChar());
10                  try {
11                       FileOutputStream out = new FileOutputStream(System.getProperty("user.dir")+"\\"+"result.txt");  
12                       OutputStreamWriter outWriter = new OutputStreamWriter(out, "UTF-8");  
13                       BufferedWriter bufWrite = new BufferedWriter(outWriter);
14                       bufWrite.write(wordCount.getFile()+" "+"字符数:"+" "+wordCount.getNumofChar());       
15                       bufWrite.close();  
16                       outWriter.close();  
17                       out.close(); 
18                         } 
19                     catch (Exception e) {
20                           // TODO Auto-generated catch block
21                         System.out.println("找不到result.txt");
22                       }
23              }

  2)WC.java

 1 public WC(String theCommand){
 2         //划分-a等参数
 3         this.flag=new int[4];
 4         String pattern = "((-[cwlsa] )+)([^(-[cwlsa] )]+(\\.).+)";   
 5         // 创建 Pattern 对象
 6         Pattern r = Pattern.compile(pattern);
 7         // 现在创建 matcher 对象,m.group(1)存储了-a等参数,m.group(3)存储了文件路径以后的内容
 8         Matcher m = r.matcher(theCommand);
 9         if(m.find()){
10             // 划分group(3)的字符串,path记录了源文件路径和-e,-o参数
11             String[] path=m.group(3).split(" -e ");
12             //设置统计的源文件
13             this.file=path[0];
14             if(path.length>1){
15                 //存在-e参数时
16                 if(path[1].split(" -o ").length>0){
17                     this.stopList=path[1].split(" -o ")[0];
18                 }
19                 //存在-o参数时
20                 if(path[1].split(" -o ").length==2){
21                     this.outputFile=path[1].split(" -o ")[1];
22                 }
23             }
24             /*
25             //确定文件路径
26             fileFinder finder = new fileFinder();  
27             List<String> filenameList = new ArrayList<String>();
28             
29             if(file.split("\\*").length>1)
30             //文件中含有*
31             {
32                 if(file.split("\\*")[0].length()>0){
33                 //文件中含有绝对路径
34                 finder.findFiles(file.split("\\.")[1] ,file.split("\\*")[0] , filenameList);}
35                 //文件默认为当前路径
36                 else{finder.findFiles(file.split("\\.")[1] ,System.getProperty("user.dir"), filenameList);}
37             }
38             else{
39             //文件中没有*,即文件名是确定的,默认\为目录的路径
40                 if(file.split("\\\\").length==1){
41                     //文件为当前路径
42                         
43                 }
44                 else{
45                     //文件为绝对路径
46                     
47                 }
48                 
49             }
50             */
51             //没有-s参数,即只用处理该目录下的制定文件
52             if(Pattern.matches("[^(-s )]*(-c )[^(-s )]*",m.group(1))){
53                 countChar(file);
54                 flag[0]=1;
55             }
56             if(Pattern.matches("[^(-s )]*(-w )[^(-s )]*",m.group(1))){
57                 countWord(file);
58                 flag[1]=1;
59             }
60             if(Pattern.matches("[^(-s )]*(-l )[^(-s )]*",m.group(1))){
61                 countLine(file);
62                 flag[2]=1;
63             }
64             if(Pattern.matches("[^(-s )]*(-a )[^(-s )]*",m.group(1))){
65                 countCode(file);
66                 flag[3]=1;
67                 countEmpty(file);
68                 countComment(file);
69             }

  3)count类型函数,下面只展示字符的统计,其他均只用更改正则表达式和某些参数

 1     //计算每个文件的字符数
 2     public void countChar(String theFile){
 3         readFileByLines readFile= new readFileByLines(this.file);
 4         //按行读取指定文件
 5         ArrayList<String>  lineArray = new ArrayList<String> ();
 6         lineArray=readFile.fileString();
 7         String pattern=".";
 8         Pattern r = Pattern.compile(pattern);
 9         int count=0;
10         for(int i =0;i<lineArray.size();i++){
11             Matcher m = r.matcher(lineArray.get(i));
12             //System.out.println(lineArray.get(i)+lineArray.size());
13             while(m.find()){
14                 count++;
15             }
16         }
17         setNumofChar(count+lineArray.size());
18         
19     }

  4)readFileByLines.java

 1     public ArrayList<String> fileString(){
 2     File file = new File(fileName);
 3     ArrayList<String>  lineArray = new ArrayList<String> ();
 4     BufferedReader reader =null;{
 5     try {
 6         reader = new BufferedReader(new FileReader(file));
 7         String tempString = null;
 8         // 一次读入一行,直到读入null为文件结束
 9         while ((tempString = reader.readLine()) != null) {
10             lineArray.add(tempString);
11         }
12         reader.close();
13     } catch (IOException e) {
14         //e.printStackTrace();
15         System.out.println("找不到指定文件,请重新输入");
16     } finally {
17         if (reader != null) {
18             try {
19                 reader.close();
20             } catch (IOException e1) {
21             }
22         }
23     }
24 }
25     return lineArray;
26     }

  5)fileFinder.java

 1     public void findFiles(String filenameSuffix, String currentDirUsed,  
 2             List<String> currentFilenameList) {  
 3         File dir = new File(currentDirUsed);  
 4         if (!dir.exists() || !dir.isDirectory()) {  
 5             System.out.println("不存在该目录");
 6             return;  
 7         }  
 8   
 9         for (File file : dir.listFiles()) {  
10             if (file.isDirectory()) {  
11                 /** 
12                  * 如果目录则递归继续遍历 
13                  */  
14                 findFiles(filenameSuffix,file.getAbsolutePath(), currentFilenameList);  
15             } else {  
16                 /** 
17                  * 如果不是目录。 
18                  * 那么判断文件后缀名是否符合。
19                  */  
20                 if (file.getAbsolutePath().endsWith(filenameSuffix)) {  
21                     currentFilenameList.add(file.getAbsolutePath());  
22                 }  
23             }  
24         }  
25     }  
26   

六、测试用例的设计:
  1)确定测试方法:根据第二周课程的学习内容,主要应用白盒测试;

  2)分析高风险点:命令行输入参数的合法性;命令行参数的划分和获取;文件名的合法性和存在性;程序中正则表达式的正确性;目录的处理;读写文件的编码问题;特殊字符的处理;

  3)测试代码的设计思路:根据程序的处理流程,找到各个判断分支,修改相应的参数以达到最大的覆盖,完成程序流程的正确性;在统计文件中输入特殊字符,检测程序正则表达式的健壮性;具体流程可以分为两个方面,一为命令行参数的测试,二为文件内容的测试。

  4)具体测试代码详见github项目地址。

七、参考文献:

  https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/0013752340242354807e192f02a44359908df8a5643103a000

  http://www.runoob.com/java/java-regular-expressions.html

  https://www.cnblogs.com/xiaoxiaohui2015/p/5838674.html

  https://www.cnblogs.com/franson-2016/p/5728280.html

  http://www.sharejs.com/codes/java/5970

  http://mouselearnjava.iteye.com/blog/1959690

  https://www.cnblogs.com/1175429393wljblog/p/5918150.html

版权声明:本文为aier02原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/aier02/p/8613128.html