《構建之法》——第四次做業

Github項目地址 Github項目地址
這個做業要求在哪裏 做業要求的連接
結對同伴的連接 同伴連接
個人博客地址 個人地址

結對編程



這是,咱們在結對編程實現關鍵代碼,在兩我的的齊心合力之下最終實現了關鍵代碼html

PSP表格

PSP2.1 Personal Software Process Stages 預估耗時(分鐘)   實際耗時(分鐘)  
Planning 計劃 40 55
· Estimate · 估計這個任務須要多少時間 90 90
Development 開發 60 75
· Analysis · 需求分析 (包括學習新技術) 30 35
· Design Spec · 生成設計文檔 15 18
· Design Review · 設計複審 (和同事審覈設計文檔) 20 20
· Coding Standard · 代碼規範 (爲目前的開發制定合適的規範) 5 5
· Design · 具體設計 30 40
· Coding · 具體編碼 120 150
· Code Review · 代碼複審 40 60
· Test · 測試(自我測試,修改代碼,提交修改) 50 70
Reporting 報告 100 120
· Test Report · 測試報告 50 55
· Size Measurement · 計算工做量 20 20
· Postmortem & Process Improvement Plan · 過後總結, 並提出過程改進計劃 15 10
  合計 685 823

1、項目思路及其總結

  • a.解題思路描述。
    • 拿到題目後,仔細閱讀理清題意。題目須要咱們對文本中的字符、單詞、單詞頻率、有效總行數進行統計
    • 進行模塊劃分除主函數以外只須要一個類,七個函數,每一個函數不一樣的功能。其次根據我的編程能力的強弱進行分配
    • 先將待統計的文檔讀入,利用正則表達式統計出符合題目要求的單詞並放入字典並返回
    • 將字典轉換成數組,並根據頻率排序從高到低進行排序,若頻率同樣按照字母順序
    • 對數組中的字符、單詞、單詞頻率、有效總行進行統計
    • 對每一個功能進行封裝,使代碼更簡潔
  • b.設計實現過程
    大致上,除了主函數這個類,還有一個大類getFile(),在getFile這個類中有七個方法,有一個公共方法getDic 這個方法用於得到字典,存入字典中的是長度大於四且不以數
    字開頭的單詞以及他們出現的次數,這個方法會返回一個Hashtable,方法getWordFre()方法將字典按照單詞出現的次數進行排序,並返回一個動態數組。
    其餘的,能夠直接調用,getWordFre這個方法利用返回的數組進行相應功能的實現。單元測試是對這幾個方法所
    對應的功能進行相應的測試。如下是整個程序的流程圖
    git

  • c.代碼規範
    • 適當使用空行,來增長代碼的可讀性
    • 方法的命名,通常將其命名爲動賓短語,一個方法只完成一個任務
    • 經常使用縮進和換行,使代碼層次清晰,明瞭
    • 對泛型進行循壞時,儘可能foreach
    • 縮進和間隔:縮進用TAB,不用 SPACES
    • 註釋需和代碼對齊
    • 避免寫太長的方法。一個典型的方法代碼在1~25行之間。
  • d.代碼說明
public Hashtable getDic(string pathName, ref Hashtable wordList)     //getDic:從文本文件中統計詞頻保存在Hashtable中
        {
            StreamReader sr = new StreamReader(pathName);
            string line;
            line = sr.ReadLine();             //按行讀取
            while (line != null)
            {
                MatchCollection mc;
                Regex rg = new Regex("[0-9A-Za-z-]+");    //用正則表達式匹配單詞
                mc = rg.Matches(line);
                for (int i = 0; i < mc.Count; i++)
                {
                    Regex regNum = new Regex("^[0-9]");
                    string mcTmp = mc[i].Value.ToLower();    //大小寫不敏感
                    if (mcTmp.Length >= 4 && regNum.IsMatch(mcTmp) == false)//字符長度大於4且不以數字開頭
                    {

                        if (!wordList.ContainsKey(mcTmp))     //第一次出現則添加爲Key
                        {
                            wordList.Add(mcTmp, 1);

                        }
                        else                                            //不是第一次出現則Value加
                        {
                            int value = (int)wordList[mcTmp];
                            value++;
                            wordList[mcTmp] = value;
                        }
                    }
                    else
                        continue;
                }
                line = sr.ReadLine();
            }
            sr.Close();
            return wordList;
        }

getDic(string pathName, ref Hashtable wordList)這個方法用於從文本中將每一個詞提取出來,並統計出每一個詞詞頻放到Hashtable中,而後用StreamReader打開文件,
用while實現按行讀取,在循環體中,用正則表達式匹配每一行的單詞,while中的for循環用於對匹配出來的單詞進行按條件剔除,符合條件的加入字典,不符合的剔除,最後返回一個Hashtablegithub

public ArrayList getWordFre(string pathName, ref Hashtable wordList)
        {
            getFile Wordlist = new getFile();

            Hashtable Wordlist_fre = new Hashtable();

            Wordlist_fre = Wordlist.getDic(pathName, ref wordList);
            ArrayList keysList = new ArrayList(Wordlist_fre.Keys);
            keysList.Sort();
            string tmp = String.Empty;
            int valueTmp = 0;
            for (int i = 1; i < keysList.Count; i++)
            {
                tmp = keysList[i].ToString();
                valueTmp = (int)wordList[keysList[i]];//次數
                int j = i;
                while (j > 0 && valueTmp > (int)wordList[keysList[j - 1]])
                {
                    keysList[j] = keysList[j - 1];
                    j--;
                }
                keysList[j] = tmp;//j=0
            }


            return keysList;
        }

getWordFre(string pathName, ref Hashtable wordList)將傳遞過來的wordList進行按頻率排序,並將Hashtable轉換成動態數組並返回正則表達式

public void write(string outputPath, ref Hashtable wordList, int lines, int words, int characters, int wordsOutNumFla, int wordsOutNum,int m,string inputPath)
        {
            getFile Wordlist = new getFile();
            ArrayList keysList = new ArrayList();
            ArrayList keysList1 = new ArrayList();

            keysList1 = Wordlist.getPhrase(inputPath, outputPath, ref wordList,  m);
            keysList = Wordlist.getWordFre(outputPath, ref wordList);
            StreamWriter sw = new StreamWriter(outputPath);
            sw.WriteLine("characters:{0}", characters);
            sw.WriteLine("words:{0}", words);
            sw.WriteLine("lines:{0}", lines);
            if (wordsOutNumFla == 1)
            {
                wordsOutNum = wordsOutNum;
            }
            else
                wordsOutNum = 10;
            for (int i = 0; i < wordsOutNum; i++)
            {
                sw.WriteLine("<{0}>:{1}", keysList[i], wordList[keysList[i]]);
            }
            sw.WriteLine("如下是長度爲{0}的詞組:\n",m);
            foreach (string j in keysList1)
            {
                sw.WriteLine("<{0}>:{1}", j, 1);
            }
            sw.Flush();
            sw.Close();
        }

寫入文件仍是比較簡單,可是有一個小細節就是在打開文件以後必定要關閉所打開的文件,否則若是要對文件進行二次追加寫入的時候回報錯,

我以前分兩次寫入文件的,而後又忘記了在第一次打開文件以後進行關閉,致使了報錯必定要記住

這個方法,傳入了須要寫入文件的總字符數、單詞數、頻率,以及頻率最高的單詞的個數的標誌位wordsOutNumFla,

經過wordsOutNumFla這個來判斷是輸出默認的十個最高頻率單詞,仍是使用-n參數後面的數字
編程

  • e 收穫
    • 一開始拿着這個題目頭都大了說個實話,首先是由於我對C#是很陌生的,隨後和個人同伴一塊兒針對此次做業如何實現進行查閱必定的相關資料,

      知道了在C#中對於統計文本中的單詞能夠藉助字典來實現,因此又去查找了字典相關的資料,而後慢慢的瞭解了該如何去實現咱們想要的功能。其次由於此次的我的做業是結對編程,

      能夠兩個一塊兒來實現代碼模塊,相比較於一我的的話就會輕鬆不少,由於兩我的結對編程的話,一我的寫,一我的審查,若是有問題能夠及時糾正,明白本身出錯的地方,

      而若是是我本身一我的寫的話,每每只能經過編譯器報錯來告訴我哪裏錯了,本身一我的是很難發現的,這樣就能夠節省必定的時間。在解決項目問題的關鍵代碼上,這部分花的時間不少,

      第一個是由於剛學的知識應用不熟悉,第二個由於本身技術確實不到位,可是經過大量的時間最終仍是作出來了,因此世上無難事只怕有心人吧。


    • 在我第一次實現代碼的時候,是沒有進行模塊的劃分的,就只有兩個類,一個程序入口,另外一個類實現全部功能,這也就致使我本身的代碼,很是亂,很是複雜,

      我本身都找不到相應的功能是在哪裏進行實現的,若是運行報錯,找半天都找不到錯誤的地方,就浪費了大量的時間,而後第二次我將個人代碼進行了模塊的劃分,

      劃分出了七個方法,每一個方法獨立,同時能夠調用其餘的方法,這樣一來,就比我第一次的代碼看着簡潔了許多。

2、單元測試

  • 在沒有封裝以前,咱們對各自的代碼進行了代碼互審
  • 對getHangNum進行測試,一下是代碼
[TestMethod]
public void getHangNum()
        {
            int lines;
            int m = 3;
            string input_path = "C:/Users/羅偉誠/Desktop/input.txt", out_put = "C:/Users/羅偉誠/Desktop/out.txt";

            Hashtable wordList = new Hashtable();
            ArrayList keysList = new ArrayList();
            getFile c = new getFile();

            keysList = c.getWordFre(input_path, ref wordList);

            lines = c.getHangNum(input_path);

           
        }



測試出來如上圖所示,沒有問題c#

  • 對 getWordNum1進行測試
[TestMethod]
 public void getWordNum1()
        {
            int words;
            int m = 3;
            string input_path = "C:/Users/羅偉誠/Desktop/input.txt", out_put = "C:/Users/羅偉誠/Desktop/out.txt";

            Hashtable wordList = new Hashtable();
            Hashtable wordList1 = new Hashtable();
            ArrayList keysList = new ArrayList();
            getFile c = new getFile();

            keysList = c.getWordFre(input_path, ref wordList);
            words = c.getWordNum(input_path);

        }
  • 對getCharactersNum1進行測試
[TestMethod]
public void getCharactersNum1()
        {
            int  words, characters = 0, wordsOutNum = 0, wordsOutNumFla = 0, inputPathFla = 0, outputPathFla = 0;
            int m = 3;
            string input_path = "C:/Users/羅偉誠/Desktop/input.txt", out_put = "C:/Users/羅偉誠/Desktop/out.txt";

            Hashtable wordList = new Hashtable();
            Hashtable wordList1 = new Hashtable();
            ArrayList keysList = new ArrayList();
            getFile c = new getFile();

            keysList = c.getWordFre(input_path, ref wordList);
            words = c.getWordNum(input_path);

        }

  • 三個測試寫在一個類中,一塊兒進行測試,測出來都經過了
  • 以前三個測試都是用的同一個文件進行測試的,接下來,用準備的十個測試樣例進行上述操做
  • 代碼測試覆蓋率,因爲這個是離線版,沒有測試覆蓋率

3、異常處理

  • 關於路徑的異常處理,由於這裏須要用戶輸入路徑,又沒法用Direct 這個路徑檢查函數來判斷,因此我就本身設置了一個標誌位
try
            {
                if (inputPathFla == 1 || outputPathFla == 1)
                {
                    Hashtable wordList = new Hashtable();
                    Hashtable wordList1 = new Hashtable();
                    ArrayList keysList = new ArrayList();
                    getFile c = new getFile();

                    keysList = c.getWordFre(input_path, ref wordList);

                    lines = c.getHangNum(input_path);

                    words = c.getWordNum(input_path);

                    characters = c.getCharactersNum(input_path);

                    c.write(out_put, ref wordList, lines, words, characters, wordsOutNumFla, wordsOutNum,m,input_path );

                    Console.WriteLine("寫入文件完成,請前往{0}查看\n", out_put);
                    
               }
                else
                {
                   Console.WriteLine("請使用 -i 參數和 -o 參數指定輸入和輸出路徑\n");
               }
            
             

        }

            catch (Exception e)
            {
                Console.WriteLine("請檢查輸入路徑是否正確");
            }

  • 這是路徑輸入正常的狀況

  • 這是路徑輸入錯誤的狀況

4、代碼改進

  • 一開始其實沒有想着用字典的,由於想着用本身已有的知識看能不能解決,可是用字符串和數組都很差解決,會致使代碼過長,冗雜
  • 使用字典以後,我只須要用一個類,將文本文檔中的數據提取出來存入字典,以後的操做能夠直接使用字典就好了,很是的方便。
  • 統計字符,以前採用的方法效率很低,後來採用的正則表達式,提升了效率
  • 效率分析及最耗時的函數以下

5、代碼複審

  • 一開始由於我不是用的正則表達式,來提取單詞,在張鵬的提示下,學習了一點點關於正則表達式的知識,並應用於程序中,由於以前我本身的沒有用正則表達式的代碼沒有保存,因此附上如今的正則表達式的代碼
  • 用正則表達式,在處理數據比較多的時候,能夠提升處理的速度(因此仍是很感謝張鵬同窗的提醒)
MatchCollection mc;
                    Regex rg = new Regex("[A-Za-z]+");    //用正則表達式匹配單詞
                    mc = rg.Matches(line);
                    for (int i = 0; i < mc.Count - m + 1; i++)
                    {
                        Regex regNum = new Regex("^[0-9]");
                        string mcTmp = "";
                        int t = i;
                        for (int q = 0; q < m; q++)
                        {
                            mcTmp += mc[t].Value.ToLower() + " ";
                            t++;
                        }
                        k.Add(mcTmp);
                    }

6、總結

經過此次結對編程,總結了一下結對編程的好處數組

  • 能夠互相監督,不容易偷懶:兩我的一塊兒工做須要互相配合,若是偷懶就會拖延進度
  • 能夠互相學習,兩我的的編程基礎不同,想法也不同,在某些方面可能我厲害些,有些方面他厲害些,因此能夠相互促進
  • 多雙眼睛,更少的bug:兩我的相互監督工做,能夠加強代碼質量,並減小BUG
  • 結對編程確實能夠達到1+1>2的效果
相關文章
相關標籤/搜索