天天看點

第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 估計這個任務需要多少時間,并規劃大緻工作步驟
  Development 開發 810 1030
 Analysis 需求分析 (包括學習新技術) 90 130
Design Spec 生成設計文檔
Design Review 設計複審 (和同僚稽核設計文檔)
 Coding Standard 代碼規範 (為目前的開發制定合适的規範)
Design 具體設計
Coding 具體編碼 480 600
 Code Review 代碼複審
Test  測試(自我測試,修改代碼,送出修改) 70 80
  Reporting 報告 180 210
Test Report 測試報告 100 120
Size Measurement 計算工作量
Postmortem & Process Improvement Plan 事後總結, 提出過程改進計劃
功能子產品 具體階段 預計時間(min) 實際時間(min)
  功能1 測試完善 20

30

200

25

功能2 140

15

150

  功能3

45

250

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次作業
第1次作業
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     }      

View Code

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)

作業輸出樣例:

第1次作業

測試輸出樣例:

第1次作業

3.個人感想

       通過此次項目的經曆,我覺得代碼的規範性很重要。這次作業我相當于寫了兩個版本,最終的代碼是在初期代碼的基礎上改了很多,這個改代碼的時間真的快趕上我寫出程式的時間了。一開始自己沒想着如何整體對項目結構進行設計,就一步步按照自己的想法寫完了,但是回過頭去改的時候,發現結構有些亂,改的時候需要兼顧很多東西,真的不太好。這讓我想到了《建構之法》第四章所講的有關代碼規範的重要性,現在看自己編寫的代碼都别扭與複雜,更别說在合作的過程中他人看自己的代碼的感受了。希望自己可以通過此次作業,在這方面有所改進。