碼云地址:
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相同的目錄下。
詳細分析:
?功能 -c?? :返回文件 file.c 的字符數
?功能 -w?? :返回文件 file.c 的單詞總數
?功能 -l???? :返回文件 file.c 的總行數
?功能 -o??? :將結果輸出到指定文件outputFile.txt
?要求 : 1. -o 功能后面必須有輸出文件路徑的參數。
2.在沒有 -o 命令指定輸出文件的時候默認將結果保存在exe同目錄下的result.txt。
3.可以滿足多個文件同時查詢。
注意:
空格,水平制表符,換行符,均算字符。
由空格或逗號分割開的都視為單詞,且不做單詞的有效性校驗,例如:thi#,that視為用逗號隔開的2個單詞。
?
二、程序設計
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類型),參數為輸入文件的集合。代碼如下:


public String ReturnCharacters(Set<String> file_paths) {int Count = 0,bytes = 0;String result = "";//用于存儲返回值byte [] tem = new byte[20*1024];//用存儲讀取數據的定常字節數組int len = tem.length;//得到tem的長度以避免循環時反復調用.lengthFileInputStream in = null;//聲明一個文件輸入流try {for (String file_path : file_paths) {in = new FileInputStream(file_path);//得到字符輸入流,string為文件絕對路徑 while ((bytes = in.read(tem,0,len))!=-1) {Count+=bytes;//統計累計讀取的字符數 }result += file_path+",字符數:"+Count+" ";//結果字符串拼接Count = 0;} } catch (FileNotFoundException e) {System.out.println("有文件輸入錯誤,請核對!(如果不會使用相對路徑,請使用絕對路徑)"); //檢查到文件不存在,提示錯誤System.exit(0); //結束程序} catch (IOException e) {e.printStackTrace();}finally {try {in.close();//關閉輸入流} catch (IOException e) {e.printStackTrace();}}return result;}
ReturnWords方法,返回查詢結果(String類型),參數為輸入文件的集合。代碼如下:


1 public String ReturnWords(Set<String> file_paths){ 2 int Count = 0; //存儲單詞數 3 String result = "";//存儲返回值 4 StringBuffer saveString = new StringBuffer();//因為string具有不可變性,用StringBuffer來進行讀取的添加 5 String tmp = ""; //緩存字符串 6 FileInputStream in = null;//聲明文件字符輸入流 7 InputStreamReader isr = null;//聲明字節輸入流 8 BufferedReader bis = null;//聲明緩存輸入流 9 try { 10 for (String file_path : file_paths) { //foreach循環遍歷數組 11 in = new FileInputStream(file_path);//實例化文件輸入流對象 12 isr = new InputStreamReader(in);//實例化字節輸入流對象 13 bis = new BufferedReader(isr);//實例化緩存輸入流對象 14 while ((tmp=bis.readLine())!=null) { //readLine()返回讀取的字節,若沒有了就返回null 15 saveString.append(tmp); //將新讀出來的數據接在已保存的后面 16 } 17 tmp = saveString.toString(); //用字符串存儲,以用split方法區分單詞 18 String [] total = tmp.split("[\\s+,\\.\n]");//用正則表達式將字符串按單詞格式分開 19 Count = total.length; //字符串數組長度就是單詞個數 20 result += file_path+",單詞數:"+Count+" "; //結果字符串拼接 21 Count = 0; 22 } 23 } catch (FileNotFoundException e) { 24 System.out.println("有文件輸入錯誤,請核對!(如果不會使用相對路徑,請使用絕對路徑)"); //檢查到文件不存在,提示錯誤 25 System.exit(0); //結束程序 26 } catch (IOException e) { 27 e.printStackTrace(); 28 }finally { 29 try { 30 in.close();//關閉文件字符輸入流 31 isr.close();//關閉字節輸入流 32 bis.close();//關閉緩存輸入流 33 } catch (IOException e) { 34 e.printStackTrace(); 35 } 36 37 } 38 return result; 39 }
ReturnLines方法,返回查詢結果(String類型),參數為輸入文件的集合。代碼如下:


1 public String ReturnLines(Set<String> file_paths) { 2 int Count = 0; 3 String result = ""; 4 FileInputStream in = null;//聲明文件字符輸入流 5 InputStreamReader isr = null;//聲明字節輸入流 6 BufferedReader bis = null;//聲明緩存輸入流 7 try { 8 for (String file_path : file_paths) { //foreach循環遍歷數組 9 in = new FileInputStream(file_path);//實例化文件輸入流對象 10 isr = new InputStreamReader(in);//實例化字節輸入流對象 11 bis = new BufferedReader(isr);//實例化緩存輸入流對象 12 while (bis.readLine()!=null) { 13 Count++; 14 } 15 result += file_path+",行數:"+Count+" "; //結果字符串拼接 16 Count = 0; 17 } 18 } catch (FileNotFoundException e) { 19 System.out.println("有文件輸入錯誤,請核對!(如果不會使用相對路徑,請使用絕對路徑)"); //檢查到文件不存在,提示錯誤 20 System.exit(0); //結束程序 21 } catch (IOException e) { 22 e.printStackTrace(); 23 }finally { 24 try { 25 in.close();//關閉文件字符輸入流 26 isr.close();//關閉字節輸入流 27 bis.close();//關閉緩存輸入流 28 } catch (IOException e) { 29 e.printStackTrace(); 30 } 31 32 } 33 return result; 34 }
OutputFile方法,將結果保存在文件中,返回boolean型,參數為文件路徑和內容。代碼如下:


1 public boolean OutputFile(String File_path,String Context){ 2 File OutputFile = new File(File_path); //創建File對象 3 FileOutputStream os = null; //聲明 文件輸出流 4 byte [] a = null; //用于存儲Context轉化的byte字節數組 5 try { 6 if(!OutputFile.exists()) { //判斷文件是否存在 7 OutputFile.createNewFile(); //不存在,創建一個文件 8 } 9 os = new FileOutputStream(OutputFile); //獲得輸出流對象 10 a = Context.getBytes(); //將Context轉化為Byte數組,以便寫入文件 11 os.write(a); //將byte數組寫入文件 12 } catch (IOException e) { 13 e.printStackTrace(); 14 }finally { 15 try { 16 os.close(); //關閉輸出流 17 } catch (IOException e) { 18 e.printStackTrace(); 19 } 20 } 21 return true; 22 } 23 }
?
四、代碼測試
代碼調試方法參照 https://www.cnblogs.com/xy-hong/p/7197725.html
由于是使用的main函數的入口參數,所以之前并未接觸過這類方式的程序編寫。在經過查閱后在改文章上找到了調試方式。具體操作再次不予詳述,請參照以上鏈接。
? 因為要求不能用已有得測試框架進行測試,所以就自己設計了一個測試類。在類中,分為單獨針對每一個需要測試得方法都設計一個測試方法,然后再主函數中調用達到一次性全部測試得效果。
import java.util.HashSet; import java.util.Set;public class ProTest {public static void main(String[] args) {testOutputFile();testReturnCharacters();testReturnLines();testReturnWords();}public static void testReturnCharacters() {Set<String> file_paths = new HashSet<String>();String file1 = "F://test.c";String file2 = "F://test.java";file_paths.add(file2);file_paths.add(file1);System.out.println("testReturnCharacters():");System.out.println(new CountUtil().ReturnCharacters(file_paths));}public static void testReturnWords() {Set<String> file_paths = new HashSet<String>();String file1 = "F://test.c";String file2 = "F://test.java";file_paths.add(file2);file_paths.add(file1);System.out.println("testReturnWords():");System.out.println(new CountUtil().ReturnWords(file_paths));}public static void testReturnLines() {Set<String> file_paths = new HashSet<String>();String file1 = "F://test.c";String file2 = "F://test.java";file_paths.add(file2);file_paths.add(file1);System.out.println("testReturnLines():");System.out.println(new CountUtil().ReturnLines(file_paths));}public static void testOutputFile() {String file_path = "result.txt";String context = "OutPutFile test !";if(new CountUtil().OutputFile(file_path, context)) {System.out.println("File output success !");}else {System.out.println("error !");}} }
?
直接運行測試類,得到結果:
?
用例測試:
文件中的結果:
?
?
?
?
?
?
五、將項目導出為exe可執行文件
依賴工具:exe4j
? 參考方法:手把手教你如何把java代碼,打包成jar文件以及轉換為exe可執行文件(注意:改博客中沒有指出32位和64位系統的差別,具體如下)
六、項目與遠程庫同步過程(碼云)
有關git和碼云項目的遠程連接以及工作提交等相關操作參考Git和Github簡單教程(該教程非常的詳細,值得推薦。)
?本項目一共有三次項目提交,分別是初始代碼、復審代碼、測試后修改的最終代碼。
七、個人總結
第一次撰寫博客有很多迷茫之處,但是在撰寫博客的過程中我發現這其實是對整個項目過程的一次回顧與反思。在以前的作業中,通常都是寫完代碼測試完后就不再關注,也不會反在完成項目的途中犯過什么樣的錯誤。每當下次遇到同樣的問題還是會被困擾住,所以我覺得寫博客是真的有必要的。雖然寫出來的博客沒人看,沒人會在意,但是這并不是開始學習的我們的目的。我們的目的就在于回顧過程,反思過程中的錯誤,讓項目的過程在我們腦海里留下更深的印象。最后,這次的作業的量還是算比較大的,但是卻真的能感覺到有很多的收獲。很多東西都是詞不達意的,真的用心體會過就能明白。
? 感謝以上引用的各鏈接的作者們,希望你們的文章能幫助更多的人。