軟件的需求分析
程序處理用戶需求的模式為:
- wc.exe [parameter][filename]
在[parameter]中,用戶通過輸入參數與程序交互,需實現的功能如下:
1、基本功能
- 支持 -c 統計文件字符數
- 支持 -w 統計文件單詞數
- 支持 -l 統計文件總行數
2、拓展功能
- 支持 -a 返回高級選項(代碼行 空行 注釋行)
- 支持 -s 遞歸處理符合條件的文件
3、高級功能
- 支持 -x 程序以圖形界面與用戶交互
[filename] 是待處理文件名。
基本功能的實現
我們先來討論基本功能的實現,在基本功能中,用戶通過輸入命令行的方式與程序實現交互。如 wc.exe -c -w -l D:\hello.txt 命令是實現統計 D 盤下 hello.txt 文件的字數、單詞數以及總行數;而 wc.exe -l D:\hello.txt 命令是實現統計 D 盤下 hello.txt 文件的總行數;可以發現,用戶每次輸入的參數個數可能會是不同的,所以我們首先要做的就是從用戶的輸入命令中分理出命令參數和文件名。
可以用如下代碼實現這一功能,在這段代碼中,我們將用戶輸入的字符串截取成字符串數組,數組的最后一位為文件名,其他位的均為命令參數。
1 string message = ""; // 存儲用戶命令 2 while (message != "exit") 3 { 4 Console.Write("wc.exe "); 5 message = Console.ReadLine(); // 得到輸入命令 6 string[] arrMessSplit = message.Split(' '); // 分割命令 7 int iMessLength = arrMessSplit.Length; 8 string[] sParameter = new string[iMessLength-1]; 9 // 獲取命令參數數組 10 for (int i=0; i< iMessLength-1; i++) 11 { 12 sParameter[i] = arrMessSplit[i]; 13 } 14 // 獲取文件名 15 string sFilename = arrMessSplit[iMessLength - 1]; 16 }
接下來,我們新建一個類 WC 來對命令進行處理,類的基本框架如下:
1 public class WC 2 { 3 private string sFilename; // 文件名 4 private string[] sParameter; // 參數數組 5 private int iCharcount; // 字符數 6 private int iWordcount; // 單詞數 7 private int iLinecount; // 總行數 8 9 // 參數控制信息 10 public void Operator(string[] sParameter, string sFilename) 11 { 12 } 13 // 統計基本信息:字符數 單詞數 行數 14 private void BaseCount(string filename) 15 { 16 } 17 // 打印信息 18 private void Display() 19 { 20 } 21 }
在Operator()方法中,捕捉 "-c"、"-w"、"-l" 命令,通過參數素組的設置調用不同的類方法進行處理;Display()方法用來打印輸出信息; BaseCount() 方法用以統計指定文件的字符數、單詞數以及總行數。
我們首先填充 Operator() 方法,此方法在參數數組中包含 "-c" 或 "-w" 或 "-l" 時調用 BaseCount() 方法實現文件基本信息的統計,調用 Display() 方法打印結果。代碼如下:
1 // 控制信息 2 public void Operator(string[] sParameter, string sFilename) 3 { 4 this.sParameter = sParameter; 5 this.sFilename = sFilename; 6 7 foreach (string s in sParameter) 8 { 9 // 基本功能 10 else if (s == "-c" || s == "-w" || s == "-l") 11 { 12 break; 13 } 14 else 15 { 16 Console.WriteLine("參數 {0} 不存在", s); 17 break; 18 } 19 } 20 }
在 BaseCount() 中,通過傳入的文件名對文件進行讀取。並進行字符數、單詞數、總行數的判斷。判斷規則如下
- 對文件進行逐字符的讀取,每讀取一個字符,則字符數加1
- 在讀取到 '\n' 字符時,判定文件總行數加1
- 在讀取到單詞分割符時,判定文件單詞數加1。單詞分割符可以是空格,制表符和各類標點符號
1 // 統計基本信息:字符數 單詞數 行數 2 private void BaseCount(string filename) 3 { 4 try 5 { 6 // 打開文件 7 FileStream file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); 8 StreamReader sr = new StreamReader(file); 9 int nChar; 10 int charcount = 0; 11 int wordcount = 0; 12 int linecount = 0; 13 //定義一個字符數組 14 char[] symbol = { ' ', '\t', ',', '.', '?', '!', ':', ';', '\'', '\"', '\n', '{', '}', '(', ')', '+' ,'-', 15 '*', '='}; 16 while ((nChar = sr.Read()) != -1) 17 { 18 charcount++; // 統計字符數 19 20 foreach (char c in symbol) 21 { 22 if(nChar == (int)c) 23 { 24 wordcount++; // 統計單詞數 25 } 26 } 27 if (nChar == '\n') 28 { 29 linecount++; // 統計行數 30 } 31 } 32 iCharcount = charcount; 33 iWordcount = wordcount + 1; 34 iLinecount = linecount + 1; 35 sr.Close(); 36 } 37 catch (IOException ex) 38 { 39 Console.WriteLine(ex.Message); 40 return; 41 } 42 }
接下來,我們只需要將統計信息打印出來,就完成了基本功能的實現。
1 // 打印信息 2 private void Display() 3 { 4 foreach (string s in sParameter) 5 { 6 if (s == "-c") 7 { 8 Console.WriteLine("字 符 數:{0}", iCharcount); 9 } 10 else if (s == "-w") 11 { 12 Console.WriteLine("單 詞 數:{0}", iWordcount); 13 } 14 else if (s == "-l") 15 { 16 Console.WriteLine("總 行 數:{0}", iLinecount); 17 } 18 } 19 Console.WriteLine(); 20 }
擴展功能的實現
在擴展功能中,用戶通過輸入命令行的方式與程序實現交互。並且在基本功能的基礎上新增了2個命令參數;wc.exe -s -c -w D:\*.txt 實現統計 D 盤下所有文件名是txt文件的字符數與單詞數的統計。wc.exe -a -c -w D:\hello.txt 實現 D 盤 hello.txt 文件 空行數、代碼行數、注釋行數以及字符數和單詞數的統計。
在這里,我們需要擴展WC類的結構
1 public class WC 2 { 3 private string sFilename; // 文件名 4 private string[] sParameter; // 參數數組 5 private int iCharcount; // 字符數 6 private int iWordcount; // 單詞數 7 private int iLinecount; // 行 數 8 private int iNullLinecount; // 空行數 9 private int iCodeLinecount; // 代碼行數 10 private int iNoteLinecount; // 注釋行數 11 12 // 初始化 13 public WC() 14 { 15 this.iCharcount = 0; 16 this.iWordcount = 0; 17 this.iLinecount = 0; 18 this.iNullLinecount = 0; 19 this.iCodeLinecount = 0; 20 this.iNoteLinecount = 0; 21 } 22 23 // 控制信息 24 public void Operator(string[] sParameter, string sFilename) 25 { 26 this.sParameter = sParameter; 27 this.sFilename = sFilename; 28 29 foreach (string s in sParameter) 30 { 31 // 遍歷文件 32 if (s == "-s") 33 { 34 } 35 // 高級選項 36 else if (s == "-a") 37 { 38 } 39 // 基本功能 40 else if (s == "-c" || s == "-w" || s == "-l") 41 { 42 ... 43 } 44 else 45 { 46 ... 47 } 48 } 49 } 50 51 // 統計基本信息:字符數 單詞數 行數 52 private void BaseCount(string filename) 53 { 54 ... 55 } 56 57 // 統計高級信息:空行數 代碼行數 注釋行數 58 private void SuperCount(string filename) 59 { 60 } 61 // 打印信息 62 private void Display() 63 { 64 ... 65 } 66 }
修改 Operator() 方法,讓其能捕捉到 "-a" 命令,實現高級選項(代碼行、空行、注釋行的統計),代碼如下:
1 // 高級選項 2 else if (s == "-a") 3 { 4 Console.WriteLine("文件名:{0}", sFilename); 5 SuperCount(sFilename); 6 BaseCount(sFilename); 7 Display(); 8 break; 9 }
在 SuperCount() 中,通過傳入的文件名對文件進行讀取。進行代碼行、空行、注釋行的統計。判斷規則如下:
- 空行:本行全為空行或是格式控制字符,若包含代碼,則只有不超過一個可顯示字符,如 "{";
- 注釋行:本行開頭除去多余空格或格式控制字符,以 ”//" 開頭,或單字符+ "\\" 開頭的行;
- 代碼行:除去空行和注釋行的其他行。
代碼如下
1 // 統計高級信息:空行數 代碼行數 注釋行數 2 private void SuperCount(string filename) 3 { 4 try 5 { 6 // 打開文件 7 FileStream file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); 8 StreamReader sr = new StreamReader(file); 9 String line; 10 int nulllinecount = 0; 11 int codelinecount = 0; 12 int notelinecount = 0; 13 while ((line = sr.ReadLine()) != null) 14 { 15 // 除去每行開頭多余空格和格式控制字符 16 line = line.Trim(' '); 17 line = line.Trim('\t'); 18 // 空行 19 if (line == "" || line.Length <= 1) 20 { 21 nulllinecount++; 22 } 23 // 注釋行 24 else if(line.Substring(0, 2) == "//" || line.Substring(1, 2) == "//") 25 { 26 notelinecount++; 27 } 28 // 代碼行 29 else 30 { 31 codelinecount++; 32 } 33 } 34 iNullLinecount = nulllinecount; 35 iCodeLinecount = codelinecount; 36 iNoteLinecount = notelinecount; 37 sr.Close(); 38 } 39 catch (IOException ex) 40 { 41 Console.WriteLine(ex.Message); 42 return; 43 } 44 }
再次修改 Operator() 方法,讓其能捕捉到 "-s" 命令,遞歸處理符合條件的文件,代碼如下:
1 // 遍歷文件 2 if (s == "-s") 3 { 4 try 5 { 6 string[] arrPaths = sFilename.Split('\\'); 7 int pathsLength = arrPaths.Length; 8 string path = ""; 9 10 // 獲取輸入路徑 11 for (int i = 0; i < pathsLength - 1; i++) 12 { 13 arrPaths[i] = arrPaths[i] + '\\'; 14 15 path += arrPaths[i]; 16 } 17 18 // 獲取通配符 19 string filename = arrPaths[pathsLength - 1]; 20 21 // 獲取符合條件的文件名 22 string[] files = Directory.GetFiles(path, filename); 23 24 foreach (string file in files) 25 { 26 Console.WriteLine("文件名:{0}", file); 27 SuperCount(file); 28 BaseCount(file); 29 Display(); 30 } 31 break; 32 } 33 catch (IOException ex) 34 { 35 Console.WriteLine(ex.Message); 36 return; 37 } 38 }
高級功能的實現
在高級功能中,用戶單獨輸入 "-x" 參數,調用圖形界面實現單個文件的選中,並輸出文件信息。首先,我們修改主函數,讓其可以接受單個 "-x" 命令。
1 static void Main(string[] args) 2 { 3 string message = ""; 4 5 while (message != "exit") 6 { 7 Console.Write("wc.exe "); 8 // 得到輸入命令 9 message = Console.ReadLine(); 10 message = message.Trim(' '); 11 message = message.Trim('\t'); 12 if (message != "-x") 13 { 14 // 分割命令 15 string[] arrMessSplit = message.Split(' '); 16 int iMessLength = arrMessSplit.Length; 17 string[] sParameter = new string[iMessLength - 1]; 18 // 獲取命令參數 19 for (int i = 0; i < iMessLength - 1; i++) 20 { 21 sParameter[i] = arrMessSplit[i]; 22 } 23 // 獲取文件名 24 string sFilename = arrMessSplit[iMessLength - 1]; 25 // 新建處理類 26 WC newwc = new WC(); 27 newwc.Operator(sParameter, sFilename); 28 } 29 else 30 { 31 string[] sParameter = new string[1]; 32 sParameter[0] = message; 33 WC newwc = new WC(); 34 newwc.Operator(sParameter, ""); 35 } 36 } 37 }
接下來,在 Operator() 函數中增加對於 “-x" 參數的捕捉,實現調用 OpenFileDialog() 類調用文件選擇對話框。
1 if(s == "-x") 2 { 3 string resultFile = ""; 4 OpenFileDialog fd = new OpenFileDialog(); 5 fd.InitialDirectory = "D:\\Patch"; 6 fd.Filter = "All files (*.*)|*.*|txt files (*.txt)|*.txt"; 7 fd.FilterIndex = 2; 8 fd.RestoreDirectory = true; 9 if (fd.ShowDialog() == DialogResult.OK) 10 { 11 resultFile = fd.FileName; 12 Console.WriteLine("文件名:{0}", resultFile); 13 SuperCount(resultFile); 14 BaseCount(resultFile); 15 DisplayAll(); 16 } 17 break; 18 }
注意:在主函數入口前加入 [STAThread] ,否則對話框不會顯示。
1 class Program 2 { 3 [STAThread] 4 static void Main(string[] args) 5 { 6 ... 7 } 8 }
最后,我們新增 DisplayAll() 函數打印全部信息
1 private void DisplayAll() 2 { 3 foreach (string s in sParameter) 4 { 5 Console.WriteLine("字 符 數:{0}", iCharcount); 6 Console.WriteLine("單 詞 數:{0}", iWordcount); 7 Console.WriteLine("總 行 數:{0}", iLinecount); 8 Console.WriteLine("空 行 數:{0}", iNullLinecount); 9 Console.WriteLine("代碼行數:{0}", iCodeLinecount); 10 Console.WriteLine("注釋行數:{0}", iNoteLinecount); 11 } 12 Console.WriteLine(); 13 }
至此,我們基本實現了 wc.exe 的基本功能、擴展功能和功能。