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