合做者:201731062301 201731062304(學號)html
本次做業連接:https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass1/homework/2882git
1.結對使用的Github項目地址:https://github.com/qili12/WordCount.gitgithub
2.結對夥伴的做業地址:http://www.javashuo.com/article/p-wuudyphs-mr.html正則表達式
找好結對的小夥伴,根據代碼功能模塊的要求,進行模塊編寫的分工。編程
按照掌握知識的熟練程度以及對不一樣知識掌握運用能力來分工合做完成這個項目。框架
PSP2.1 |
Personal Software Process Stages |
預估耗時(分鐘) |
實際耗時(分鐘) |
Planning |
計劃 | 45 |
45 |
· Estimate | · 估計這個任務須要多少時間 | 45 | 45 |
Development |
開發 | 910 |
1295 |
· Analysis | · 需求分析 (包括學習新技術) | 45 | 60 |
· Design Spec | · 生成設計文檔 | 30 | 45 |
· Design Review | · 設計複審 (和同事審覈設計文檔) | 20 | 60 |
· Coding Standard | · 代碼規範 (爲目前的開發制定合適的規範) | 30 | 30 |
· Design | · 具體設計 | 45 | 60 |
· Coding | · 具體編碼 | 600 | 800 |
· Code Review | · 代碼複審 | 60 | 90 |
· Test | · 測試(自我測試,修改代碼,提交修改) | 80 | 150 |
Reporting |
報告 | 90 |
115 |
· Test Report | · 測試報告 | 30 | 45 |
· Size Measuremen | · 計算工做量 | 25 | 25 |
· Postmortem & Process Improvement Plan | · 過後總結, 並提出過程改進計劃 | 35 | 45 |
合計 | 1045 |
1455 |
項目目標:實現命令行程序。ide
一、統計字符數,行數,讀入文件的時候有不少種讀入方式,好比ReadLine,ReadToEnd等等,選擇一行一行讀入,同時記錄行數,而且記錄每一行的有效字符串統計長度做爲字符數。模塊化
二、統計單詞頻率。首先須要判斷是否爲單詞,採用正則表達式。把全部符合單詞的要求的單詞放入一個list當中,再用字典進行頻率統計和排序。函數
三、輸出到文件。仍是採用字典排序,將單詞輸出到一個文件。單元測試
先把大概的框架梳理清楚,再去查找相應的資料。
在設置相應的命令行參數的時候,-i做爲輸入的文件路徑,必須存在。在輸入其餘參數的時候要判斷是否符合,不作無效輸出。
使用一個WordList類封裝統計字符,單詞頻率、單詞個數、按字典序輸出到文件四個個方法,在WordCount中調用。
具體代碼思路:
C#項目代碼規範:http://www.javashuo.com/article/p-qautokas-mr.html
在這一塊不是很懂,感受在效能分析上好像有些問題,沒有找到緣由。
消耗最大的函數:
static void Main(string[] args) { int countLine = 0; string str = ""; string path = ""; int phraseNum = 0; int wordFreNum = 0; for (int i = 0; i < args.Length; i += 2) // 判斷輸入參數 { switch (args[i]) { /* -i 參數設定讀入文件的路徑*/ case "-i": path = args[i + 1]; break; /* -m 參數設定的詞組長度*/ case "-m": phraseNum = int.Parse(args[i + 1]); break; /* -n 參數設定輸出單詞數量*/ case "-n": wordFreNum = int.Parse(args[i + 1]); break; /* -o 參數設定生成文件的存儲路徑*/ case "-o": break; } } //當文件路徑存在時 if (File.Exists(path)) { StreamReader sr = new StreamReader(path, Encoding.Default); string line; while ((line = sr.ReadLine()) != null) { countLine++; str += line + "\n"; } sr.Close(); str = str.Trim(); //若是含有-o參數 將顯示內容輸出到文件中 for (int i = 0; i < args.Length; i++) { if (args[i] == "-o") { FileStream fs = new FileStream(args[i + 1], FileMode.Create); StreamWriter sw = new StreamWriter(fs); sw.WriteLine("Characters:" + WordsList.CountChar(str)); sw.WriteLine("Lines: " + countLine); sw.WriteLine("Words:" + WordsList.CountWords(str)); //若是有-n參數且有大於零的輸入,調用PutNwords函數 if (wordFreNum>0) { sw.WriteLine("輸出頻率前"+wordFreNum+"的詞組:"); Dictionary<string, int> item = PutNwords(str).OrderByDescending(r => r.Value).ThenBy(r => r.Key).ToDictionary(r => r.Key, r => r.Value); int size = 0; foreach (KeyValuePair<string, int> entry in item) { string word = entry.Key; int frequency = entry.Value; size++; if (size > wordFreNum) break; sw.WriteLine(word + ":" + frequency); } } //若是有-n參數且大於零的輸入,則調用phraseNum函數 if(phraseNum > 0) { sw.WriteLine("輸出長度爲" + phraseNum + "的詞組:"); Dictionary<string, int> item = PhraseFre(str,phraseNum).OrderByDescending(r => r.Value).ThenBy(r => r.Key).ToDictionary(r => r.Key, r => r.Value); foreach (KeyValuePair<string, int> entry in item) { string word = entry.Key; int frequency = entry.Value; sw.WriteLine(word + ":" + frequency); } } sw.Flush();//關閉流 sw.Close(); Console.WriteLine("文件已建立在:" + args[i + 1]); } } } else Console.WriteLine("沒有文件路徑或文件不存在!"); }
WordList的類中方法:
判斷單詞是否符合要求(使用正則表達式來判斷):
//判斷單詞是否符合要求,若符合用列表存儲。 public static List<string> Judge(string path) { List<string> list = new List<string>(); string[] wordsArr1 = Regex.Split(path, "\\s*[^0-9a-zA-Z]+"); foreach (string word in wordsArr1) { if (Regex.IsMatch(word, "^[a-zA-Z]{4,}[a-zA-Z0-9]*"))//判斷前4個是否爲字母 { list.Add(word); } } return list; }
統計單詞頻率(用字典存儲方式,能夠很好地取用鍵值對):
//統計單詞詞頻並輸出前10 public static void CountWordFre(string path) { List<string> list = new List<string>(); list = Judge(path); Dictionary<string, int> frequencies = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); foreach (string word in list) { if (frequencies.ContainsKey(word)) { frequencies[word]++; } else frequencies[word] = 1; } Dictionary<string, int> item = frequencies.OrderByDescending(r => r.Value).ThenBy(r => r.Key).ToDictionary(r => r.Key, r => r.Value); int size = 0;//限定輸出個數 foreach (KeyValuePair<string, int> entry in item) { string word = entry.Key; int frequency = entry.Value; size++; if (size > 10) break; Console.WriteLine(word + ":" + frequency); } }
統計詞組頻率(僅考慮詞組的相對位置只要是先後關係便可)
private static Dictionary<string,int> PhraseFre(string path,int n) { List<string> list = new List<string>(); list = WordsList.Judge(path);//list是符合單詞要求單詞的集合 Dictionary<string, int> frequencies = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); string s = ""; for (int i = 0; i <= list.Count - n; i++)//將長度爲n的詞組當成一個字符串 { int j; for (s = list[i], j = 0; j < n - 1; j++) { s += " " + list[i + j + 1]; } if (frequencies.ContainsKey(s)) { frequencies[s]++; } else frequencies[s] = 1; } return frequencies; }
命令行程序執行結果:
一、代碼複審
在寫完各自分工的模塊後,進行代碼複審。主要在編程規範和單元測試兩個方面,對代碼不易讀懂的地方進行代碼註釋,修改命名方式。
對程序集中的公開類和公共屬性進行單元測試。
二、問題
在單元測試方面主要針對的是對單詞是否符合要求的判斷(在咱們的代碼過程當中是重點),經過測試發現輸入的文件中的單詞不能爲空。
若爲空值,則會觸發異常,但其實是容許爲空,但不執行後續代碼。
三、解決:使用了不正確的斷言方式,輸入空值的時候應該斷言list中是否爲空。
四、在單元測試的編寫上,看了一些關於單元測試編寫的例子,對單元測試有必定的瞭解,可是在編寫的過程當中仍是不知要如何下手,單元測試的代碼覆蓋率應該要達到100%,顯然本次的單元測試並非合格的,還有不少須要改進和學習的方法,寫出好的單元測試。
[TestClass] public class UnitTest1 { [TestMethod] public void TestCountChar() { string str = "Abcd aa!"; int count = str.Length; Assert.AreEqual(WordsList.CountChar(str), count); string str1 = ""; Assert.AreEqual(WordsList.CountChar(str1), str1.Length); } [TestMethod] public void TestJudgeWords() { string str = null; Assert.IsNull(WordsList.Judge(str)); } [TestMethod] public void TestCountWord() { string str = null; List<string> list = new List<string>(); list = WordsList.Judge(str); Assert.IsNull(list); } }
在具體編碼過程當中,對程序修改了不少次,主要是在程序的模塊化,使得相互之間能夠更好地調用對方的代碼。經過此次結對編程發現1+1>2,每一個人對同一個問題都有不一樣的解題思路和想法,兩我的在一塊兒完成一項做業能夠更好地交流對問題的見解,找到一些更好地解決辦法。相比較於本身編程,思路出現錯誤可能會卡殼好久,兩我的進行結對,能夠相互學習,拓展編程思路,更快的完成。