第三次作業-結對編程(wordcount)


GIT地址 https://github.com/gentlemanzq/WordCount.git
GIT用戶名  gentlemanzq
結對伙伴博客地址
博客地址  https://www.cnblogs.com/gentlemanzq/
作業鏈接 https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass1/homework/2882

這一次結對編程,怎么說呢。帶來了一次不一樣的編程體驗,很難說清楚。具體后面再說,先看作業


  • 一.結對過程

照片如下:(一直很疑惑為什么后台更改的照片,前端不會更改,百度了也沒有解決辦法。將就看吧)


  • 二.PSP表格

PSP2.1

Personal Software Process Stages

預估耗時(分鍾)

實際耗時(分鍾)

Planning

計划

 30  20

· Estimate

· 估計這個任務需要多少時間

 30  20

Development

開發

 670  740

· Analysis

· 需求分析 (包括學習新技術)

 180  180

· Design Spec

· 生成設計文檔

 20  10

· Design Review

· 設計復審 (和同事審核設計文檔)

 20  20

· Coding Standard

· 代碼規范 (為目前的開發制定合適的規范)

 30  30

· Design

· 具體設計

 60  60

· Coding

· 具體編碼

180  200

· Code Review

· 代碼復審

 60  80

· Test

· 測試(自我測試,修改代碼,提交修改)

 120  160

Reporting

報告

 85  105

· Test Report

· 測試報告

 45  60

· Size Measurement

· 計算工作量

 20  15

· Postmortem & Process Improvement Plan

· 事后總結, 並提出過程改進計划

 20  30
 

合計

 785  865


  • 三.解題思路

  1.首先拿到題目仔細閱讀,理清題意。大致需要完成的功能都是關於字符操作和計數排序的操作。根據這幾點需求,我們決定使用泛型Dictionary來完成這一系列操作。

  2.關於如何解題,是根據作業要求來的,首先需要將文件讀入成一個字符串,然后對其進行操作,如果是統計字符數量和行數,需要借助Regex函數通過正則表達式 ‘’.‘’來統計除換行符之外的字符,最后加上換行符。(此處要注意換行符是兩個字符)

  3.判斷是否是單詞的時候,先使用for循環將大小寫統一轉換為小寫,重新定義一個新的Dictionary,然后使用笨辦法if語句進行判斷單詞長度是否超過4個並且前四個是否是英文,如果滿足條件則重新賦給新的dictionary。

  4.輸出頻率最高的10個詞,如果頻率最高則按字典序輸出這個地方。將新的dictionary先用frequencies函數進行統計判斷,然后將結果首先按照value值進行排序,然后再按照key值進行排序

  5.對每一個功能項進行封裝,方便后面增加功能。

  6.考慮到以上涉及的知識,所以我們在編碼前,首先參考了字典的用法,如何進行單詞判斷,如何按照key值和value值進行排序(參考這兩篇博客【1】【2】)。其次是關於regex,正則表達式的使用(參考這篇博客【3】)。

  【1】:https://www.cnblogs.com/wt-vip/p/5997094.html

  【2】:https://blog.csdn.net/ybhjx/article/details/69668442

  【3】:https://blog.csdn.net/u012102536/article/details/85160138


  • 四.代碼設計及接口封裝設計

  1.首先對於每一個功能大致設置一個類,初步設計六個類,將除了program類其余放入function文件夾中,具體關系后面說明(PS:增加功能之后再添加)

 

  2.類與類之間的調用關系具體為:program類中調用path,linescount,asccount類。在asccount類中會調用linescount參與部分計算。wordcount調用ynword進行判斷

  3.啟動主函數在program類里面,如果要計算有多少個字符就調用ascount里面的agelife方法,如果要統計有多少行,就調用linescount中的lines方法。同理其余都是一樣。

  4.基礎功能的難點在於判斷是否是單詞,並且需要按照次數,字典序排序,在wordcount函數中先進行是否單詞判斷,此處調用ynword。具體設計見流程圖

  5.單元測試設計,主要測試function文件夾中的功能函數,類圖如下 (PS:單元測試代碼后見代碼復審)

  6.接口設計及特色,由於功能都具有各自特色,相互影響性不高,故將每個功能都單獨成塊,抽離出來。功能都單獨返回值,不會在功能中輸出。並將有用的參數通過ref傳出。

  7.算法設計關鍵:行數 總字符數都是通過正則表達式的方式進行統計,判斷單詞出現頻率,調用字典的封裝好的函數。在判斷是否是單詞時,將文本讀成字符串,字符串再通過正則表達式拆分成字符串數組。


  • 五.代碼規范

  1.   不要冗余無用代碼,過於冗余的代碼可以清理一下,一些已經注釋掉的代碼可以刪除

  2、不變的值,盡量寫個常量類。

  3、盡量使用if{}else,不要一直if去判斷。

  4、減少循環調用方法;減少IO流的消耗資源。

  5.   當一行代碼太長時,將其截斷成兩行寫。

  6.   常用縮進和換行,使代碼層次清晰,明了。

  7.   注釋的量不應該少於代碼量的三分之一。ps(變量統一使用例如/// <param name="s">文件讀入路徑</param>的注釋方式)

  8.   定義變量名字和方法名字的時候盡量使用英文縮寫,或者拼音縮寫,便於識別。

  9.   對泛型進行循環時,都采用foreach而不使用for。

  11. 對於功能函數寫入一個function文件夾中,便於以后功能升級。

  12. 一屏原則:一個方法體的代碼幅應該在一屏比較和合理;邏輯復雜的代碼可以抽離出方法體。


  • 六.代碼復審及部分單元測試

  1.在沒有封裝功能前,我們各自對對方寫的代碼進行第一次互審。耗時:20min

  2.在進行封裝之后,我們一起針對幾個模塊功能進行審查,首先針對邏輯上第一個調用的計算行數的功能模塊linescount。經過二人的審查,覺得代碼沒有問題。為了測試正確,此處進行單元測試。

public class linescountTests
    {
        [TestMethod()]
        public void linesTest()
        {
            path.s = @"D:\se.txt";
            int x = 0;//第一次測試時輸入5,第二次輸入0
            Assert.AreEqual(x, linescount.lines());
           // Assert.Fail();
        }
    }

  測試結果如下:此處第一次在記事本中輸入兩行測試成功,但是在輸入0行時測試失敗,此處出現大問題,當沒有輸入文本時,行數沒有進行判斷,所以出現錯誤。

  3.審查asccount類(ps:功能為統計有多少字符),經過檢查之后,並未發現問題,於是進行單元測試。

 public class asccountTests
    {
        [TestMethod()]
        public void asccountsTest()
        {
            path.s = @"D:\se.txt";
            int num = 8;
            Assert.AreEqual(num, asccount.asccounts());
            //Assert.Fail();
        }
    }

  測試結果如下:當定義num=0,文本不輸入字符時,出現測試錯誤。反應過來依舊是沒有判斷為零情況,所以才會出現錯誤。

  4.根據邏輯思維,由於想要審查countword類必須要先審查ynword,保證其正確性,故先審查ynword功能模塊,經過前兩次錯誤,此次審查小心謹慎,依舊沒有發現問題。故接着進行單元測試

 public void ynword1Test()
        {
            int w = 1;
            string[] n = { "word1" };
            string[] newword = { "word1" };
            string[] test = ynword.ynword1(n, ref w);
            Assert.AreEqual(newword[0],test[0] );
        }

  5.使用10個不同測試樣例,重復以上操作

 

  6.代碼測試覆蓋率,由於這個是社區版,沒有測試覆蓋率。

 


  • 七.異常處理

  1.關於輸入路徑,輸出路徑異常處理(暫時只想到路徑異常)

 try
                {
                    for (int i = 0; i < args.Length; i++)
                    {
                        if (args[i] == "-i") path.s = args[++i];//-i 命令行
                        else if (args[i] == "-n") max = Convert.ToInt32(args[++i]);//-n 命令行
                        else if (args[i] == "-o") path.outputpath = args[++i];//-o 命令行
                        else if (args[i] == "-m")
                        {
                            len = Convert.ToInt32(args[++i]);
                        }
                    }
                }
                catch
                {
                    Console.WriteLine("輸入或者輸出的路徑有誤");
                }
//---------------------------------------------------------
 try
                {
                    Console.WriteLine("不輸入參數,請手動輸入讀入文件路徑");
                    string s = Console.ReadLine();
                    path.s = s;
                    max = 10;
                    Console.WriteLine("請手動輸入輸出路徑");
                    string s1 = Console.ReadLine();
                    path.outputpath = s1;
                }
                catch
                {
                    Console.WriteLine("輸入或者輸出的路徑有誤");
                }

 

  未處理:

 

  處理后:

 


 

  • 八.改進代碼

  1.改進所用時間:45min+。

  2.剛開始結對編程的時候,第一時間想用數組,字符串數組來寫的,但是在進行一定的編碼后,覺得數組太麻煩,實在是不適合,故最后決定使用字典泛型。

  3.使用字典編程之后,剛開始所有功能模塊都寫在一起,代碼耦合性太差了,后由於要增加功能,並且要進行封裝接口,故把所有的功能模塊都抽離出來,寫入function文件夾

  4.剛開始統計行數和字符總數時,用的另外的方法,后面改進使用正則表達式的方式,提高效率。

  5.見效率分析圖及最耗時函數

 


 

  • 九.部分代碼展示

  1.統計行數展示:ps:需要注意0行的情況

 public static int lines()//統計文件中的行數
        {
            string str = File.ReadAllText(@path.s);
            int nr = Regex.Matches(str, @"\r").Count ;
            if (nr != 0)
                nr = nr + 1;
            return nr;
        }

  2.統計總字符個數展示:ps:需要注意換行符是/r/n兩個字符。“.”只能統計/r不能統計/n故加上行數。需注意0個字符的情況。

 public static int asccounts()//打開文件並統計字符個數
        {
            string str = File.ReadAllText(@path.s);
            int num = Regex.Matches(str, @".").Count;
            if (linescount.lines() == 0)
                return num + linescount.lines();
            else
                return num + linescount.lines() - 1;
        }

  3.統計單詞個數展示:ps:在這里統計單詞數需要對每一個進行判斷,調用了ynword,此處不展示,用的笨辦法if多次判斷

public static Dictionary<string, int> Countword()
        {
            
            string str = File.ReadAllText(@path.s);
            Dictionary<string, int> frequencies = new Dictionary<string, int>();
            string[] words = Regex.Split(str, @"\W+");
            int k = 0;
            string[] newwords = ynword.ynword1(words,ref k);
            string[] newwords1 = new string[k];
            for (int i = 0; i < k; i++)
            {
                newwords1[i] = newwords[i];
            }
            foreach (string word in newwords1)
            {

                if (frequencies.ContainsKey(word))
                {
                    frequencies[word]++;
                }
                else
                {
                    frequencies[word] = 1;
                }
            }
            return frequencies;
        }

  4.主函數展示: ps:此處由於增加功能,通過查閱資料,知道了命令行輸入是存入args中的,故通過這種方式進行操作。

  static void Main(string[] args)
        {
            int temp = 0;
            int max = 0;
            int len = 0;
       //如果命令行有參數執行
if (args.Count() != 0) { for (int i = 0; i < args.Length; i++) { if (args[i] == "-i") path.s = args[++i];//-i 命令行 else if (args[i] == "-n") max = Convert.ToInt32(args[++i]);//-n 命令行 else if (args[i] == "-o") path.outputpath = args[++i];//-o 命令行 else if (args[i] == "-m") { len = Convert.ToInt32(args[++i]); } } if (path.s == null || path.outputpath == null)//路徑為空則不存在 { Console.WriteLine("路徑不正確,文件不存在"); } }
       //命令行無參數,執行
else { Console.WriteLine("不輸入參數,請手動輸入讀入文件路徑"); string s= Console.ReadLine(); path.s = s; max = 10; Console.WriteLine("請手動輸入輸出路徑"); string s1 = Console.ReadLine(); path.outputpath = s1; } Dictionary<string, int> frequencies = function.wordcount.Countword();//調用wordcount中方法統計單詞 Dictionary<string, int> dic1Asc = frequencies.OrderBy(o => o.Key).ToDictionary(o => o.Key, p => p.Value);//按照字典序進行排序 int sum = function.wordcount.sum1(dic1Asc);//計算出單詞總數量 Console.WriteLine("字符數:"+asccount. asccounts());//計算出字符數量 Console.WriteLine("單詞總數:" + sum); Console.WriteLine("行數:"+linescount. lines());//計算出行數 //先按照出現次數排序,如果次數相同按照字典序排序 Dictionary<string, int> dic1Asc1 = frequencies.OrderByDescending(o => o.Value).ThenBy(o => o.Key).ToDictionary(o => o.Key, p => p.Value); foreach (KeyValuePair<string, int> entry in dic1Asc1) { if (temp == max) break; string word = entry.Key; int frequency = entry.Value; temp++; Console.WriteLine("{0}:{1}", word, frequency); } Console.ReadKey(); }

  • 十.總結

剛開始覺得結對編程沒有太大的用處,但是通過這次結對編程的經歷之后,感覺了結對編程的好處,1+1還真是大於2的。由於剛開始陷入了誤區,糾結了許多小問題,通過搭檔的提醒,一下子豁然開朗,一個人的主觀思維是不完美的,只有通過和他人的合作,才能使得代碼趨緊無錯,滿足所有情況。有了一個實時搭檔,在編碼時在旁邊指出自己的不足和思維漏洞,這樣提高了效率,降低了檢查時重來的時間。此次收獲良多。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM