Github項目鏈接 一、項目簡介 該項目是一個常見的工具,它能統計文本文件的字元數、單詞數和行數。這個項目要求寫一個命令行程式,模仿已有wc.exe 的功能,並加以擴充,實現一個統計程式,它能正確統計程式文件中的字元數、單詞數、行數,以及還具備其他擴展功能,並能夠快速地處理多個文件。 二、項目實現 ...
一、項目簡介
該項目是一個常見的工具,它能統計文本文件的字元數、單詞數和行數。這個項目要求寫一個命令行程式,模仿已有wc.exe 的功能,並加以擴充,實現一個統計程式,它能正確統計程式文件中的字元數、單詞數、行數,以及還具備其他擴展功能,並能夠快速地處理多個文件。
二、項目實現情況
•基本功能
• -c 返迴文件的字元數 (實現)
• -w 返迴文件的詞的數目 (實現)
• -l 返迴文件的行數 (實現)
•擴展功能
• -s 遞歸處理目錄下符合條件的文件(實現)
• -a 返回更複雜的數據(代碼行/空行/註釋行)(實現)
• 支持各種文件的通配符(*,?)(實現)
•高級功能
• 基本的Windows GUI 程式操作(未實現)
• 支持通過圖形界面選取文件(未實現)
• 支持通過圖形界面展現文件的信息(未實現)
三、PSP
PSP2.1 |
Personal Software Process Stages |
預估耗時(分鐘) |
實際耗時(分鐘) |
Planning |
計劃 |
30 |
42 |
· Estimate |
· 估計這個任務需要多少時間 |
30 |
42 |
Development |
開發 |
720 |
960 |
· Analysis |
· 需求分析 (包括學習新技術) |
90 |
102 |
· Design Spec |
· 生成設計文檔 |
20 |
25 |
· Design Review |
· 設計覆審 (和同事審核設計文檔) |
10 |
12 |
· Coding Standard |
· 代碼規範 (為目前的開發制定合適的規範) |
10 |
13 |
· Design |
· 具體設計 |
60 |
65 |
· Coding |
· 具體編碼 |
410 |
422 |
· Code Review |
· 代碼覆審 |
60 |
75 |
· Test |
· 測試(自我測試,修改代碼,提交修改) |
60 |
246 |
Reporting |
報告 |
120 |
136 |
· Test Report |
· 測試報告 |
90 |
95 |
· Size Measurement |
· 計算工作量 |
10 |
5 |
· Postmortem & Process Improvement Plan |
· 事後總結, 並提出過程改進計劃 |
20 |
36 |
合計 |
|
870 |
1138 |
四、解題思路
1、本項目分為基本功能、擴展功能、高級功能。除了主類,將功能類基本分為一個基本功能類、三個擴展功能類(包括計算代碼行、空行、註釋行的類和遞歸查找的類和通配符處理的類,分別對應三個擴展功能要求)
2、計算各種行或詞的方法大致思路為:首先讀入文件 -> 使用readLine()方法逐行讀取 -> 依據計算的對象不同編寫不同的計算方法
3、遞歸處理:編寫find()方法不斷遞歸,查找文件如果是文件夾返回所有文件和文件夾的絕對路徑接著再遞歸查找。如是文件則加入列表裡,最後再將一個個列表裡的對象取出處理。
4、通配符處理:將用戶輸入的所有*或?分別用.*和.?代替(正則表達式的任意字元),並使用pattern類對象存儲模式,遞歸查找符合條件的文件。
五、設計實現過程
功能簡易基本流程圖如下所示:
代碼結構圖如下所示,包括五個類(一個基本功能類,三個擴展功能類,一個properties類),一個類對應幾個方法。
六、關鍵代碼與註釋思路說明
-c 返迴文件的字元數
public void strNum(String p) { //計算字元數的方法 try { BufferedReader bw = new BufferedReader(new FileReader(p)); //讀入文件 while((c2=bw.readLine())!=null) { String k = c2.replace(" ",""); //將空格全部替換成空 a2=a2+k.length(); //每讀一行,累計字元數 sx.setStrNum(a2); } System.out.println("該文件的字元數為:"+sx.getStrNum()); bw.close(); } catch (FileNotFoundException e) { System.out.println("無法找到指定文件"); } catch (IOException e) { System.out.println("I/O錯誤"); } }
-w 返迴文件的詞的數目
public void wordNum(String p) { //計算單詞數的方法 try { BufferedReader bw = new BufferedReader(new FileReader(p)); //讀入文件 StringBuffer sb=new StringBuffer(); while((c3=bw.readLine())!=null) { //readline讀取到換行不算為null if(c3.matches("")==false) { //字元比較不能直接!=,如果讀取的行非空執行下麵語句 sb.append(c3+"\\s"); //將讀取到的內容追加在StringBuffer對象中,在結尾加上一個空白字元避免與下行的首部相連 } } c3=sb.toString(); //轉為字元串模式 String[] strings=c3.split("[^\\w]"); //以非詞字元為分隔 將詞分開 System.out.println("該文件單詞個數為:"+(strings.length-1)); bw.close(); } catch (FileNotFoundException e) { System.out.println("無法找到指定文件"); } catch (IOException e) { System.out.println("I/O錯誤"); } }
-l 返迴文件的行數
public void lineNum(String p) { //計算行數的方法 try { BufferedReader bw = new BufferedReader(new FileReader(p));//讀入文件 while(bw.readLine()!= null) { //當讀行不為空 a1++; //行數加一 sx.setLineNum(a1); } System.out.println("該文件的行數為:"+sx.getLineNum()); bw.close(); //關閉流 } catch (FileNotFoundException e) { //捕獲錯誤1 System.out.println("無法找到指定文件"); } catch (IOException e) { //捕獲錯誤2 System.out.println("I/O錯誤"); } }
-s 遞歸處理目錄下符合條件的文件
public class extended_function_recursion { //擴展功能第二部分支持 -s遞歸處理目錄下符合條件的文件 basic_function bf = new basic_function(); //創建基本功能類對象 extended_function ef = new extended_function(); //創建擴展功能類第一部分對象 public void recursion(String fileName, String filePath) { //遞歸處理目錄下符合條件的文件 List<String> l = new ArrayList<String>(); find(new File(filePath),fileName,l); String path = null; if(l.size()>=1)path = l.get(0); //提取文件路徑 System.out.println("該目錄下符合要求的文件的絕對路徑為:"+path); } public static void find(File file,String fileName,List<String> l) { if (file.isDirectory()) { //如果是一個文件夾,返回true File[] f1 = file.listFiles(); //返回某個當前目錄下的所有文件和文件夾的絕對路徑,返回的是File數組 for (File f : f1) { find(f,fileName,l); //遞歸執行 } }else{ if(file.getName().equals(fileName)){ l.add(file.getAbsolutePath()); //獲取絕對路徑 } } } }
-a 返回更複雜的數據(代碼行 / 空行 / 註釋行)
計算代碼行部分:
public void codeline(String p) { //計算代碼行方法,判斷代碼行標準:本行包括多於一個字元的代碼 try { BufferedReader bw = new BufferedReader(new FileReader(p));//讀入文件 while( (e1=bw.readLine())!= null) { if(e1.length()>=2){ //若本行內容長度多於一個字元 j1++; } sx.setCodeLine(j1); } System.out.println("該文件的代碼行數為:"+sx.getCodeLine()); bw.close(); } catch (FileNotFoundException e) { System.out.println("無法找到指定文件"); } catch (IOException e) { System.out.println("I/O錯誤"); } }
-a 返回更複雜的數據(代碼行 / 空行 / 註釋行)
計算空行部分:
public void blankline(String p) { //計算空行的方法,判斷空行標準:本行全部是空格或格式控制字元,如果包括代碼,則只有不超過一個可顯示的字元 try { BufferedReader bw = new BufferedReader(new FileReader(p)); while( (e2=bw.readLine())!= null) { o = e2.matches("\\s+||\\S"); //e2不加雙引號!!,如果讀行內容匹配多個空白字元或只有一個非空白字元,空行加一 if(o == true) { //=是錯的!! j2++; } sx.setBlankLine(j2); } System.out.println("該文件的空行數為:"+sx.getBlankLine()); bw.close(); } catch (FileNotFoundException e) { System.out.println("無法找到指定文件"); } catch (IOException e) { System.out.println("I/O錯誤"); } }
-a 返回更複雜的數據(代碼行 / 空行 / 註釋行)
計算註釋行部分:
public void commentline(String p) { //計算註釋行的方法,判斷註釋行標準:本行不是代碼行(包括多於一個字元的代碼),並且本行包括註釋,註釋前可有} try { BufferedReader bw = new BufferedReader(new FileReader(p)); while( (e3=bw.readLine())!= null) { if(e3.contains("//")) { //如果本行有// o = (e3.substring(0, e3.indexOf("//"))).matches("\\S{0,1}");//判斷//前如果為一個或零個字元則為註釋行 if(o == true) { j3++;} } else if(e3.contains("/*")){ //同理 o = (e3.substring(0, e3.indexOf("/*"))).matches("\\S{0,1}"); if(o == true) { j3++;} } else if(e3.contains("*/")){ //同理 o = (e3.substring(0, e3.indexOf("*/"))).matches("\\S{0,1}"); if(o == true) { j3++;} } sx.setCommentLine(j3); } System.out.println("該文件的註釋行數為:"+sx.getCommentLine()); bw.close(); } catch (FileNotFoundException e) { System.out.println("無法找到指定文件"); } catch (IOException e) { System.out.println("I/O錯誤"); } }
支持各種文件的通配符(*,?)部分:
public class tong_pei { //擴展功能第三部分,支持各種文件的通配符(*,?) public File[] getFiles(String dir,String s) { //getFiles方法,返回File數組存取路徑 File file = new File(dir); s = s.replace("*", ".*");//將*換為正則表達式的零次或多次的任意字元 s = s.replace("?", ".?");//將?換為正則表達式的一次或沒有的任意字元 Pattern p = Pattern.compile(s); //用compile()方法設置匹配模式 ArrayList list = filePattern(file, p);//調用filePattern方法 File[] rtn = new File[list.size()]; list.toArray(rtn); return rtn; } public ArrayList filePattern(File file, Pattern p) { if (file == null) { //如果文件為空返回空 return null; } else if (file.isFile()) { //判斷該文件是否標準文件 Matcher fMatcher = p.matcher(file.getName()); if (fMatcher.matches()) { ArrayList list = new ArrayList(); list.add(file); return list; } } else if (file.isDirectory()) { //判斷文件是否為文件夾 File[] files = file.listFiles(); if (files != null && files.length > 0) { ArrayList list = new ArrayList(); for (int i = 0; i < files.length; i++) { ArrayList rlist = filePattern(files[i], p); if (rlist != null) { list.addAll(rlist); } } return list; } } return null; } }
properties類
public class properties { //Properties類 int strNum=0; int wordNum=0; int lineNum=0; int codeLine=0; //代碼行 int blankLine=0; //空行 int commentLine=0; //註釋行 public int getCodeLine() { return codeLine; } public void setCodeLine(int codeLine) { this.codeLine = codeLine; } public int getBlankLine() { return blankLine; } public void setBlankLine(int blankLine) { this.blankLine = blankLine; } public int getCommentLine() { return commentLine; } public void setCommentLine(int commentLine) { this.commentLine = commentLine; } public int getStrNum() { return strNum; } public void setStrNum(int strNum) { this.strNum = strNum; } public int getWordNum() { return wordNum; } public void setWordNum(int wordNum) { this.wordNum = wordNum; } public int getLineNum() { return lineNum; } public void setLineNum(int lineNum) { this.lineNum = lineNum; } }
主類
import java.io.File; import java.util.Scanner; public class wc { public static void main(String[] args) { basic_function mts = new basic_function(); //以下四行創建功能類對象 extended_function wtf = new extended_function(); extended_function_recursion efr = new extended_function_recursion(); tong_pei tp = new tong_pei(); Scanner scan = new Scanner(System.in);//創建Scanner類對象 if (scan.hasNext()) { //若有輸入 String str1 = scan.nextLine(); if((str1.substring(0,2)).equals("-l"))mts.lineNum(str1.substring(3,str1.length())); if((str1.substring(0,2)).equals("-c"))mts.strNum(str1.substring(3,str1.length())); if((str1.substring(0,2)).equals("-w"))mts.wordNum(str1.substring(3,str1.length())); if((str1.substring(0,2)).equals("-a")) { wtf.codeline(str1.substring(3,str1.length())); //substring() 方法返回的子串包括 start 處的字元,但不包括 stop 處的字元 wtf.blankline(str1.substring(3,str1.length())); wtf.commentline(str1.substring(3,str1.length())); } if((str1.substring(0,2)).equals("-s")) { String arrays[] = str1.split(" "); File f[] = tp.getFiles(arrays[2],arrays[1]); //調用tong_pei類中getFiles()方法 for(File ff:f) { efr.recursion(ff.getName(),ff.getAbsolutePath()); } } if((str1.substring(0,4)).equals("-s-a")) { basic_function bf = new basic_function(); //創建基本功能類對象 extended_function ef = new extended_function(); //創建擴展功能類第一部分對象 String arrays[] = str1.split(" "); File f[] = tp.getFiles(arrays[2],arrays[1]); //調用tong_pei類中getFiles()方法 for(File ff:f) { efr.recursion(ff.getName(),ff.getAbsolutePath()); System.out.println("以下為遞歸處理目錄下符合條件的文件的屬性:"); bf.lineNum(ff.getAbsolutePath()); //以下為文件屬性 bf.strNum(ff.getAbsolutePath()); bf.wordNum(ff.getAbsolutePath()); ef.codeline(ff.getAbsolutePath()); ef.blankline(ff.getAbsolutePath()); ef.commentline(ff.getAbsolutePath()); } } scan.close(); //關閉流 } } }
七、測試運行結果
-c 返迴文件的字元數:
-w 返迴文件的詞的數目:
-l 返迴文件的行數:
-a 返回更複雜的數據(代碼行 / 空行 / 註釋行):
-s 遞歸處理目錄下符合條件(條件:用戶指定的文件)的文件:
-s 遞歸