第1次作业
要求0:作业要求地址【https://edu.cnblogs.com/campus/nenu/2016CS/homework/2110】
要求1:git仓库地址【https://git.coding.net/Jingr98/wf.git】
要求2:
1.PSP阶段表格
| 
 SP2.1  | 
 任务内容  | 
 计划共完成需要的时间(min)  | 
 实际完成需要的时间(min)  | 
| 
 Planning  | 
 计划  | 
 40  | 
 50  | 
| 
 Estimate  | 
 估计这个任务需要多少时间,并规划大致工作步骤  | 
 40  | 
 50  | 
| 
 Development  | 
 开发  | 
 810  | 
 1030  | 
| 
 Analysis  | 
 需求分析 (包括学习新技术)  | 
 90  | 
 130  | 
| 
 Design Spec  | 
 生成设计文档  | 
 40  | 
 40  | 
| 
 Design Review  | 
 设计复审 (和同事审核设计文档)  | 
 0  | 
 0  | 
| 
 Coding Standard  | 
 代码规范 (为目前的开发制定合适的规范)  | 
 0  | 
 40  | 
| 
 Design  | 
 具体设计  | 
 90  | 
 90  | 
| 
 Coding  | 
 具体编码  | 
 480  | 
 600  | 
| 
 Code Review  | 
 代码复审  | 
 40  | 
 50  | 
| 
 Test  | 
 测试(自我测试,修改代码,提交修改)  | 
 70  | 
 80  | 
| 
 Reporting  | 
 报告  | 
 180  | 
 210  | 
| 
 Test Report  | 
 测试报告  | 
 100  | 
 120  | 
| 
 Size Measurement  | 
 计算工作量  | 
 40  | 
 40  | 
| 
 Postmortem & Process Improvement Plan  | 
 事后总结, 提出过程改进计划  | 
 40  | 
 50  | 
| 
 功能模块  | 
 具体阶段  | 
 预计时间(min)  | 
 实际时间(min)  | 
| 
 功能1  | 
 具体设计 具体编码 测试完善  | 
 20 130 20  | 
 30 200 25  | 
| 
 功能2  | 
 具体设计 具体编码 测试完善  | 
 30 140 20  | 
 15 150 25  | 
| 
 功能3  | 
 具体设计 具体编码 测试完善  | 
 40 210 30 
  | 
 45 250 30  | 
2.分析预估耗时和实际耗时的差距原因:
(1)在分析预估耗时时,没有过多考虑编程中的细节问题。编程过程中不断有新的问题出现。
(2)对Java语言掌握不够熟练,编程中学习的时间较长
(3)最主要的原因就是一开始审题不清,没有考虑到输出样例格式的问题。导致项目将要完工时又要整改很多地方,浪费了好多时间。
要求3:
1.解题思路描述
(1)看到题目后,我想了一下这个程序大致有三个步骤:读取文本、统计单词、(排序)输出。有了这个框架后,我从最简单的功能1尝试编写,在获取到文本内容需要对字符串进行分割时我遇到了一些问题(因为不是很熟悉正则表达式),所以查阅了相关教程,自己尝试写了一下可以达到预期效果,但是需要两次正则表达式的运用(一是对字符串按空格和非字母数字符号进行分割得到字符串数组,二是对得到的字符串数组通过字母开头规则过滤掉那些非法单词),自我感觉这里编写的不是太好。实现了功能1后我开始看功能2,发现只要得到文件夹下的文件名数组,排序后返回指定文件路径后就可以参照功能1的实现。功能3的话只要在前者的基础上传入参数-n,对list进行排序后根据-n输出结果。
(2)最初编写时我是把功能1和功能2写在了一起,即用一个count()函数实现两个功能。功能1直接调用count()就可以实现词频统计,功能2则需要先调用readDir()和setpath()方法得到指定文件路径,然后再调用count()就可以了。功能3则是用count( int n )实现。但是后来我仔细看了题目后发现,两者的输出样例是不一样的!发现了这个问题后,我本来想写两个输出结果的方法分别对应上述两种情况,但是尝试了一下报了很多错误,就不敢大改了,只能选择把两种情况完全分开处理,于是就有了现在的 countFile()和countDir()分别对应功能1和功能2,countNum()对应功能3。这样虽然解决了样例输出的问题,但是代码重复量真的很大。
2.代码介绍
(1)困难点:功能1主要是对字符串的处理(正则表达式的运用),如何得到合法单词;功能2主要是获取某文件夹下的所有文件名,并按照文件名排序后返回指定文件,其余就参照功能1的实现;功能3主要是对词频进行排序,并按照参数进行输出。其实我感觉这三个功能分开来写不是很难,对我来说,最困难的就是如何减少代码的重复。三个功能明显有重复的部分,应该把哪些部分拎出来写成公共的方法,是我应该继续思考的!
(2)代码片段
简单介绍一下我的代码,总体上用Java写了两个类:wf类(包含各种功能方法)和wfTest类(分情况对wf类里的方法进行调用,用来测试)。
1) isLegal()函数:判断是否为合法单词
 1 public boolean isLegal(String word) {
 2         String regex="^[a-zA-Z][a-zA-Z0-9]*$";
 3         Pattern p =  Pattern.compile(regex);
 4         Matcher m =p.matcher(word);
 5         if(m.matches()) {
 6             return true;
 7         }else {
 8             return false;
 9         }
10     }
2) readDir()函数:获取文件夹下所有文件的文件名,对数组进行排序并返回第一个元素
 1 public String readDir(String filepath){
 2         File file = new File(filepath);
 3         String[] filelist = file.list();
 4         String[] namelist = new String [filelist.length];
 5         for(int i=0;i<filelist.length;i++) {
 6             File readfile = new File(filepath+"\\"+filelist[i]);
 7             namelist[i]=readfile.getName();
 8         }
 9         List<String> list = (List<String>)Arrays.asList(namelist);
10         Collections.sort(list);
11         String[] paths = list.toArray(new String[0]);
12             
13         return paths[0];
14     }
3) countFile()函数:当输入格式为【wf -c 文件名 】时调用,并输出结果
 1 public void countFile() {
 2         try {
 3             FileInputStream inputStream = new FileInputStream(new File(path));
 4             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
 5             //将文件内容存入words中
 6             while((lineword=bufferedReader.readLine())!=null) {
 7                 words+=lineword+"\n";
 8             }
 9             //全部转换成小写,达到不区分大小写的目的
10             String str=words.toString().toLowerCase();        
11             //分割字符串并存入数组
12             String[] word = str.split("[^a-zA-Z0-9]|\\ ");
13             int num =0;
14             Map<String,Integer> myMap = new TreeMap<String,Integer>();
15             //遍历数组将其存入Map<String,Integer>中
16             for(int i=0;i<word.length;i++) {    
17                 //首先判断是否为合法单词,合法则存入map中        
18                 if(isLegal(word[i])) {
19                     if(myMap.containsKey(word[i])) {
20                         num = myMap.get(word[i]);
21                         myMap.put(word[i], num+1);
22                     }
23                     else {
24                         myMap.put(word[i], 1);
25                     }
26                 }
27             }
28             //将map.entrySet()转换成list
29             List<Map.Entry<String, Integer>> list =new ArrayList<Map.Entry<String,Integer>>(myMap.entrySet());
30             //输出结果
31             System.out.println("total"+" "+list.size()+"\n");
32 //            for(int i=0;i<list.size();i++) {
33 //                Map.Entry<String, Integer> e =list.get(i);
34 //                System.out.println(e.getKey()+" "+e.getValue());
35 //            }
36             for(int i=0;i<word.length;i++) {
37                 if(myMap.containsKey(word[i])) {
38                     System.out.printf("%-14s%d\n",word[i],myMap.get(word[i]));
39                     myMap.remove(word[i]);
40                 }
41             }
42             bufferedReader.close();
43         }catch(FileNotFoundException e) {
44             e.printStackTrace();
45         }catch(IOException e) {
46             e.printStackTrace();
47         }
48     }
4) countDir()函数:当输入格式为【wf -f 文件路径 】时调用,并输出结果。(和countFile函数基本一致,只是输出格式上有些不同)
 1 public void countDir() {
 2         try {
 3             FileInputStream inputStream = new FileInputStream(new File(path));
 4             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
 5             //将文件内容存入words中
 6             while((lineword=bufferedReader.readLine())!=null) {
 7                 words+=lineword;
 8             }
 9             //全部转换成小写,达到不区分大小写的目的
10             String str=words.toString().toLowerCase();
11             //分割字符串并存入数组
12             String[] word = str.split("[^a-zA-Z0-9]|\\ ");
13             int num =0;
14             Map<String,Integer> myMap = new TreeMap<String,Integer>();    
15             //遍历数组将其存入Map<String,Integer>中
16             for(int i=0;i<word.length;i++) {    
17                 //首先判断是否为合法单词        
18                 if(isLegal(word[i])) {    
19                     if(myMap.containsKey(word[i])) {
20                         num = myMap.get(word[i]);
21                         myMap.put(word[i], num+1);
22                     }
23                     else {
24                         myMap.put(word[i], 1);
25                     }
26                 }
27             }    
28             //将map.entrySet()转换成list
29             List<Map.Entry<String, Integer>> list =new ArrayList<Map.Entry<String,Integer>>(myMap.entrySet());
30             //输出结果
31             System.out.println("total"+" "+list.size()+" words");
32             for(int i=0;i<list.size();i++) {
33                 Map.Entry<String, Integer> e =list.get(i);
34                 System.out.println(e.getKey()+" "+e.getValue());
35             }
36             bufferedReader.close();
37         }catch(FileNotFoundException e) {
38             e.printStackTrace();
39         }catch(IOException e) {
40             e.printStackTrace();
41         }
42     }
5) countNum()函数:当输入格式为【wf -f 文件路径 -n 数量】或者【wf -c 文件名 -n 数量】或者【wf -n 数量 -c 文件名】或者【wf -n 数量 -f 文件路径】时调用,并输出结果
 1 public void countNum(int n) {
 2         try {
 3             FileInputStream inputStream = new FileInputStream(new File(path));
 4             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
 5             //将文件内容存入words中
 6             while((lineword=bufferedReader.readLine())!=null) {
 7                 words+=lineword+"\n";
 8             }
 9             //全部转换成小写,达到不区分大小写的目的
10             String str=words.toString().toLowerCase();    
11             //分割字符串并存入数组
12             String[] word = str.split("[^a-zA-Z0-9]|\\ ");
13             int num =0;
14             Map<String,Integer> myMap = new TreeMap<String,Integer>();
15             //遍历数组将其存入Map<String,Integer>中
16             for(int i=0;i<word.length;i++) {    
17                 //首先判断是否为合法单词        
18                 if(isLegal(word[i])) {
19                     if(myMap.containsKey(word[i])) {
20                         num = myMap.get(word[i]);
21                         myMap.put(word[i], num+1);
22                     }
23                     else {
24                         myMap.put(word[i], 1);
25                     }
26                 }
27             }
28             //将map.entrySet()转换成list
29             List<Map.Entry<String, Integer>> list =new ArrayList<Map.Entry<String,Integer>>(myMap.entrySet());
30             //通过比较器实现排序
31             Collections.sort(list,new Comparator<Map.Entry<String, Integer>>(){
32                 public int compare(Entry<String,Integer> e1,Entry<String,Integer> e2) {
33                     return e2.getValue().compareTo(e1.getValue());
34                 }
35             });
36             //输出结果
37             System.out.println("Total words is "+list.size());
38             System.out.println("----------");
39             for(int i=0;i<n;i++) {
40                 Map.Entry<String, Integer> e =list.get(i);
41                 System.out.printf("%-14s%d\n",e.getKey(),e.getValue());
42             }
43             bufferedReader.close();
44         }catch(FileNotFoundException e) {
45             e.printStackTrace();
46         }catch(IOException e) {
47             e.printStackTrace();
48         }
49     }
6)setpath()函数:创建对象时若没有传入路径,则给变量path赋值
public void setpath(String path) { this.path=path; }
7)最后,在wfTest类里,我通过 if else 语句对控制台输入的字符串分情况讨论,调用相应的方法
 1 package wf;
 2 import java.util.*;
 3 
 4 public class wfTest{
 5     public static void main(String[] args) {
 6         Scanner input = new Scanner(System.in);
 7         String str = "";
 8         str = input.nextLine();
 9         String[] splt = str.split(" ");
10         String path;
11         int num=splt.length;
12         if(num==3) {
13             
14             if(splt[1].equals("-c")) {
15                 path=splt[2];
16                 wf test = new wf(path);
17                 test.countFile();
18             }else if(splt[1].equals("-f")){
19                 
20                 wf test = new wf();
21                 path=test.readDir(splt[2]);
22 //                System.out.println(splt[2]);
23                 test.setpath(splt[2]+"\\"+path);
24                 test.countDir();
25             }else {
26                 System.out.println("输入格式有错误");
27             }
28            
29         }else if(num==5) {
30             if(splt[1].equals("-f")&&splt[3].equals("-n")) {
31                 wf test = new wf();
32                 path=test.readDir(splt[2]);
33                 test.setpath(splt[2]+"\\"+path);
34                 test.countNum(Integer.parseInt(splt[4]));
35             }else if(splt[1].equals("-c")&&splt[3].equals("-n")) {
36                 path=splt[2];
37                 wf test = new wf(path);
38                 test.countNum(Integer.parseInt(splt[4]));
39             }else if(splt[1].equals("-n")&&splt[3].equals("-c")) {
40                 path=splt[4];
41                 wf test = new wf(path);
42                 test.countNum(Integer.parseInt(splt[2]));
43             }else if(splt[1].equals("-n")&&splt[3].equals("-f")) {
44                 wf test = new wf();
45                 path=test.readDir(splt[4]);
46                 test.setpath(splt[4]+"\\"+path);
47                 test.countNum(Integer.parseInt(splt[2]));
48             }else {
49                 System.out.println("输入格式有错误");
50             }
51         }else {
52             System.out.println("输入格式有错误");
53         }
54         
55         input.close();
56     }
57 }
8)运行结果展示:
此截图是在eclipse平台上运行的效果,但是项目里已经生成 wf.exe 执行文件,可以在控制台进行测试(这是我第一次用jar包通过exe4j生成可执行文件,因为在exe4j上忘记选择生成控制台程序(默认是gui程序),所以在这里纠结了好久,真的是。。。为了避免大家犯同样的错误,在此诚挚地推荐一篇相关博客:https://blog.csdn.net/u011752272/article/details/80697198)
作业输出样例:

测试输出样例:

3.个人感想
通过此次项目的经历,我觉得代码的规范性很重要。这次作业我相当于写了两个版本,最终的代码是在初期代码的基础上改了很多,这个改代码的时间真的快赶上我写出程序的时间了。一开始自己没想着如何整体对项目结构进行设计,就一步步按照自己的想法写完了,但是回过头去改的时候,发现结构有些乱,改的时候需要兼顾很多东西,真的不太好。这让我想到了《构建之法》第四章所讲的有关代码规范的重要性,现在看自己编写的代码都别扭与复杂,更别说在合作的过程中他人看自己的代码的感受了。希望自己可以通过此次作业,在这方面有所改进。
                    
                

                
            
        
浙公网安备 33010602011771号