第二周作業 WordCount
https://github.com/HuangDongPeng/WordCount.git
1.1 PSP
PSP2.1 |
PSP階段 |
預估耗時 (分鐘) |
實際耗時 (分鐘) |
Planning |
計劃 |
30 |
30 |
· Estimate |
· 估計這個任務需要多少時間 |
5h |
12h |
Development |
開發 |
2h |
4h |
· Analysis |
· 需求分析 (包括學習新技術) |
30min |
30min |
· Design Spec |
· 生成設計文檔 |
- |
- |
· Design Review |
· 設計復審 (和同事審核設計文檔) |
- |
- |
· Coding Standard |
· 代碼規範 (為目前的開發制定合適的規範) |
5min |
5min |
· Design |
· 具體設計 |
10 |
0 |
· Coding |
· 具體編碼 |
2h |
3h |
· Code Review |
· 代碼復審 |
10 |
10 |
· Test |
· 測試(自我測試,修改代碼,提交修改) |
2h |
3h |
Reporting |
報告 |
1h |
1h |
· Test Report |
· 測試報告 |
|
|
· Size Measurement |
· 計算工作量 |
|
|
· Postmortem & Process Improvement Plan |
· 事後總結, 並提出過程改進計劃 |
20min |
30min |
|
合計 |
8h |
12h |
2.1 WordCount需求說明
WordCount的需求可以概括為:對程序設計語言源文件統計字符數、單詞數、行數,統計結果以指定格式輸出到默認文件中,以及其他擴展功能,並能夠快速地處理多個文件。
wc.exe -c file.c //返回文件 file.c 的字符數
wc.exe -w file.c //返回文件 file.c 的單詞總數
wc.exe -l file.c //返回文件 file.c 的總行數
wc.exe -o outputFile.txt //將結果輸出到指定文件outputFile.txt
wc.exe -s //遞歸處理目錄下符合條件的文件
wc.exe -a file.c //返回更復雜的數據(代碼行 / 空行 / 註釋行)
wc.exe -e stopList.txt // 停用詞表,統計文件單詞總數時,不統計該表中的單
2.2 解題思路及分析過程
功能實現
1.單詞統計
按字符流讀取文件,對每一個字符做判斷,如果不是換行符或空格則繼續往下讀取;當讀取到換行符或者空格是,將前面讀到的字符拼作一個單詞
,單詞計數加一:
while ((tempChar = reader.read()) != -1) { if ((tempChar >= 65 && tempChar <= 90) || (tempChar >= 97 && tempChar <= 122)) { isChar = true; } else { if (isChar) { isChar = false; wordCount++; } continue; } }
2.字符計數
每讀入一個字符,判斷是不是回車換行符,不是則字符計數器加一:
while ((tempChar = reader.read()) != -1) { //判斷是不是回車 if (!(tempChar == 13 || tempChar == 10||tempChar==9)) charCount++; character = (char) tempChar; } reader.close(); }
3.行數讀取
調用java API,統計計數
reader = new BufferedReader(new FileReader(file)); String tempString = null; int line = 0; while ((tempString = reader.readLine()) != null) { line++; } reader.close();return line;
4.遞歸獲取文件
獲取文件目錄,判斷是不是目錄,如果是目錄則遞歸獲取該目錄下的內容;如果符合要求的文件,則先將文件名存儲,與之後的執行一同進行
File tmpFile = new File(dir); if (tmpFile.isDirectory()) { try { String[] fileNames = tmpFile.list(); if (fileNames.length != 0) { for (String s : fileNames) { String newPath = dir + "/" + s; FindFile(newPath); } } } catch (Exception e) { e.printStackTrace(); } } else { if (dir.contains(fomatName)) { int filePointIndex=dir.lastIndexOf("."); String rightFormatName=dir.substring(filePointIndex); if(rightFormatName.equals(fomatName)) canBeFoundFile.add(dir);//存儲符合後綴文件 } }
5.獲取不同類型的行數
獲取代碼行:非註釋、非空的都是代碼行
獲取註釋行:包含//或者包囊在 /* ..*/中的都是註釋行,按字符讀取,如果讀取到/*則進入註釋統計模式,直到讀取到*/才退出該模式繼續統計
獲取空行:不解釋
boolean isNote=false; reader = new BufferedReader(new FileReader(file)); String tempString = null; int emptyLine = 0; int codeLine = 0; int noteLine = 0; while ((tempString = reader.readLine()) != null) { if(tempString.contains("/*")){ isNote=true; } else if(tempString.contains("*/")) { isNote=false; } if (tempString.contains("//")||tempString.contains("*/")||isNote) noteLine++; else if (tempString.isEmpty() || IsEmpty(tempString)) { emptyLine++; } else codeLine++;
6.停用表功能
若存在於停用表中的單詞則統計單詞數時不計入統計。
先獲取停用表中的單詞,存儲與List,然後讀取指定文件單詞做判斷,若屬於停用表中的則單詞計數器不增加。
//讀取停用表內容
while ((tempChar = reader.read()) != -1) { if ((tempChar >= 65 && tempChar <= 90) || (tempChar >= 97 && tempChar <= 122)) { isChar = true; sb.append((char) tempChar); } else { if (isChar) { wordTable.add(sb.toString()); sb = new StringBuilder(); isChar = false; } continue; } } if (isChar && sb.length() != 0) { wordTable.add(sb.toString()); }//讀取文件內容while ((tempChar = reader.read()) != -1) { if ((tempChar >= 65 && tempChar <= 90) || (tempChar >= 97 && tempChar <= 122)) { isChar = true; localSb.append((char) tempChar); } else { if (isChar) { if (!IsInTable(wordTable, localSb.toString())) { wordCount++; } localSb = new StringBuilder(); isChar = false; } continue; }
7.輸出文件
修改輸出文件名稱,不解釋
參數分析
在知道執行哪些命令前需要遍歷一遍輸入參數,存儲要執行的指令,在統一結合遞歸獲取文件等操作執行指令
//首先遍歷一邊參數,找出需要執行的指令
for (int i = 0; i < inputArgs.length; i++) {
//如果使用了停用表指令,就獲取停用表名
if (inputArgs[i].contains("-e")) { isUseStopList = true; i++; stopListFileName = inputArgs[i]; } //是否需要重新定義輸出文件名並獲取輸出文件名 if (inputArgs[i].contains("-o")) { isOutPutFile = true; i++; outputFileName = inputArgs[i]; }
//是否遞歸搜索 if (inputArgs[i].contains("-s")) { isGetDirFiles = true; } }
//尋找目標文件,目標文件之前的都是執行參數
for (int i = 0; i < inputArgs.length; i++) { if (inputArgs[i].contains(".")) { fileNameIndex = i; filePath = inputArgs[i]; if (filePath.contains(".")) { int pointIndex = filePath.lastIndexOf("."); fomatName = filePath.substring(pointIndex); } break; } } if (!isGetDirFiles) { for (int i = 0; i < fileNameIndex; i++) { OrderJudge(inputArgs[i]); } } else { SetFileDir(inputArgs); FindFile(fileDir); for (String s : canBeFoundFile) { filePath = s; System.out.println(s); for (int i = 0; i < fileNameIndex; i++) { OrderJudge(inputArgs[i]); } } } //輸出文件 OutPutFile(outputFileName, sb); }
2.3測試
編寫測試,覆蓋每一個函數及功能,每一個的形參包括:讀取的文件名,預期的輸入;
每一個調用的輸出結果是:
測試名稱、預期結果、測試輸出結果、是否符合預期結果;
public boolean Test_CharCount(int expResult, String path) { return PrintErrorDetail(GetMethodName(), expResult, Main.ReadChar(path)); } public boolean Test_WordCount(int expResult, String path) { return PrintErrorDetail(GetMethodName(), expResult, Main.ReadWord(path)); } public boolean Test_LineCount(int expResult, String path) { return PrintErrorDetail(GetMethodName(), expResult, Main.ReadLine(path)); } public boolean Test_ReadDiffLine(int emptyLine, int noteLine, int codeLine, String path) { int[] expResult = new int[3]; expResult[0] = codeLine; expResult[1] = emptyLine; expResult[2] = noteLine; String[] lineNames = {"codeLine", "emptyLine", "noteLine"}; int[] testResult = Main.GetDifferentLine(path); for (int i = 0; i < 3; i++) { PrintErrorDetail(GetMethodName() + " " + lineNames[i], expResult[i], testResult[i]); if (expResult[i] != testResult[i]) return false; } return true; } public boolean Test_StopList(int expResult, String stoplistPath, String readFilePath) { int testResult = Main.StopWordTable(stoplistPath, readFilePath); return PrintErrorDetail(GetMethodName(), expResult, testResult); } public boolean Test_Recursion(int expFileCount, String path, String formateName) { Main.fomatName = formateName; return PrintErrorDetail(GetMethodName(), expFileCount, Main.FindFile(path)); } public boolean Test_OutputFile(StringBuilder sb, String outputPath) { System.out.println("test: " + GetMethodName()); if (Main.OutPutFile(outputPath, sb)) { System.out.println("out put file success"); return true; } System.out.println("out put file failed"); return false; } public boolean Test_Recursion_StopList(String formatName, String stopListName, int[] expCount) { Main.fomatName = formatName; Main.FindFile("./"); boolean result = true; for (int i = 0; i < Main.canBeFoundFile.size(); i++) { if (!Test_StopList(expCount[i], stopListName, Main.canBeFoundFile.get(i))) { result = false; } } return result; } public boolean Test_Recusion_WordRead_OutputFile(String formatName, String outputFileName, int[] expResult) { Main.fomatName = formatName; Main.FindFile(Main.fileDir); boolean result = true; for (int i = 0; i < Main.canBeFoundFile.size(); i++) { if (expResult[i] != Main.ReadWord(Main.canBeFoundFile.get(i))) { result = false; } } if (result) { result = Main.OutPutFile(outputFileName, Main.sb); } return result; }
第二周作業 WordCount