zoukankan      html  css  js  c++  java
  • 第三次作业-结对编程(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, @"
    ").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的。由于刚开始陷入了误区,纠结了许多小问题,通过搭档的提醒,一下子豁然开朗,一个人的主观思维是不完美的,只有通过和他人的合作,才能使得代码趋紧无错,满足所有情况。有了一个实时搭档,在编码时在旁边指出自己的不足和思维漏洞,这样提高了效率,降低了检查时重来的时间。此次收获良多。

  • 相关阅读:
    数据结构实验之链表一:顺序建立链表
    数据结构实验之链表五:单链表的拆分
    最终排名
    选夫婿1
    数据结构实验之链表三:链表的逆置
    数据结构实验之链表二:逆序建立链表
    数据结构实验之链表四:有序链表的归并
    水题
    win32线程简单封装
    Boost 1.46.1,vc2005, 编译
  • 原文地址:https://www.cnblogs.com/gentlemanzq/p/10647267.html
Copyright © 2011-2022 走看看