合作就是不断磨合的过程!

一、Fork仓库的Github项目地址和伙伴的地址

Github项目地址:https://github.com/WHYNOTEN/WordCount.git

合作同学作业地址:https://www.cnblogs.com/SW-P-WY/p/10657136.html

合作人:wnagyang、zhourong


二、PSP表格(事实上实际耗费时间比预估时间要长)

 

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

890  1435

· Estimate

· 估计这个任务需要多少时间

890  1435

Development

开发

770   1270

· Analysis

· 需求分析 (包括学习新技术)

40   50

· Design Spec

· 生成设计文档

30   35

· Design Review

· 设计复审 (和同事审核设计文档)

50   60

· Coding Standard

· 代码规范 (为目前的开发制定合适的规范)

20   15

· Design

· 具体设计

30   50

· Coding

· 具体编码

420   900

· Code Review

· 代码复审

60   40

· Test

· 测试(自我测试,修改代码,提交修改)

120   120

Reporting

报告

120   165

· Test Report

· 测试报告

 60  80

· Size Measurement

· 计算工作量

30   60

· Postmortem & Process Improvement Plan

· 事后总结, 并提出过程改进计划

30   25
 

合计

900   1435

绘制PSP表格总结:以前编写项目的时候并未有设计和预算的过程,个人感觉编写表格和事先设计好进程,更有利于完成项目。因为在完成的过程中对自己进行到那个地方心里有个数。


三.计算接口模块的设计与实现过程

 1.需求分析(由两人共同商议)

  基础功能

      1.统计文件字符数(统计字符这一块,对提取文本中字符问题,出现了乱码,后来商讨了下解决不同文本转码的问题)

    2.统计单词总数(单词必须四个英文字母开头,后面可以为数字,不区分大小写)

    3.统计文件中有效行数

    4.统计文件中单词单词总数,按频率排序,对于相同频率单词按字典顺序排列

    5.按照字典顺序输出到文件中

    6.调用程序时,通过命令行传入文件读取和写入路径

  新功能:

        1.输出前n个频率最高的单词(直接新增加一个列表保存输出的前n个单词)

         2.指定一个词组长度,输出词组及其出现频率

         3.多参数混合使用(封装)

  实现所有需求思路:

  1. 需求主要是对于文本中内容的提取,这里就要用到正则表达,之前没有涉及到过就看了下群里同学分享的链接。(用到正则的部分是伙伴编写的,个人不是很会使用正则)
  2. 对于行数提取则可以通过读取文件(按行提取内容)时,判断提取内容是否为null来解决。
  3. 统计频率可以通过字典这种Key,Value这种存储方式来进行统计,排序则通过字典排序实现。
  4. 对于命令行传入参数可以通过Main(string[] args)中对args进行解析来实现。
  5. 新功能输出前n个频率最高可以直接输出字典内容即可。
  6. 伙伴询问助教老师后确定同样可以利用字典统计词组并输出(-m的功能是和伙伴“吵了一架的”,因为他觉得构架和基础功能无异,由我编写)
  7. 第三个功能主要是对于Main方法args参数的解析,可以利用字典保存参数及其内容

最终确定主要写两个.cs文件,其中一个对功能实现,另外一个进行功能类调用,这样测试时即只需要对功能类函数进行测试即可,基本功能由笔者实现,对于新功能有合作者实现,最终整合在功能中:

Function.cs:主要实现对文本内容的提取筛选实现需求功能;

Program.cs:对命令行参数进行分析(GetMand()),调用Function.cs对文本内容分析,实现功能(Main()).

 算法关键在于正则表达式的书写以及将内容写入字典整合:

文件读取代码:

//读取文件中的字符数目并保存文件内容
        public void GetChar()
        {
            //打开文件
            FileInfo file = new FileInfo(this.path);
            //定义读取文件对象
            StreamReader sw = file.OpenText();
            //按行进行读取,不为空行记录行数并保存内容,返回null打断循环
            while (true)
            {
                //对文件读取内容进行判断,如果不为空用变量接收,行数加一
                string temp = sw.ReadLine();
                if (temp != null)
                {
                    account++;
                    this.content += temp;
                }
                //为空则停止
                else
                {
                    break;
                }

            }
        }
View Code

正则匹配函数:

public void ExtractChar()
        {
            //利用正则进行匹配,以字母开头,可以数字结尾
            MatchCollection rel = Regex.Matches(this.content, @"([a-zA-Z]{4}\w*)");
            for (int i = 0; i < rel.Count; i++)
            {
                //匹配到的单词进入列表
                this.result.Add(Convert.ToString(rel[i]));
            }
        }
View Code

字典写入代码: 

//利用字典统计单词出现次数
        public void Statistical()
        {
            //建立临时字典保存单词以及出现次数
            Dictionary<string, int> words = new Dictionary<string, int>();
            for (int i = 0; i < this.result.Count; i++)
            {
                if (words.ContainsKey(this.result[i]))
                {
                    words[this.result[i]]++;
                }
                else
                {
                    words[this.result[i]] = 1;
                }
            }
            //对字典内容排序,并赋值给类变量
            this.words_sort = words.OrderByDescending(p => p.Value).ToDictionary(p => p.Key, o => o.Value);
            //清空临时字典内容
            words.Clear();

        }
View Code

四、代码复审

  代码规范:详情请查看链接

  这部分在这次合作中我深有体会,之前在课程学习中,本人认为自己的代码自己看。符合自己规范就行,但是在这次合作中,深切地体会到代码规范的问题,好在伙伴的编程规范很好,他所编写的我都能看懂,审核。此方面我也该向他学习。

 


五、性能改进

从性能分析图看出Program.Main()与Function.ToFile()分配最多,但由于这两个函数都是调用了其余函数,所以减去其余调用函数分配,得出Function.ToFile()分配最多,为786,其次是Function.ExtractChar()(PS:之前和伙伴在-m新增功能中有争议,他说会影响性能,硬是要删除,被本人留了下来,后来代码复审的过程中我发现,确实功能和基础功能用法无异)

//将内容写入文件中
        public void ToFile(string path)
        {
            //运行先行函数,将需要的文件内容保存至变量中
            this.GetChar();
            this.ExtractChar();
            this.ToLow();
            this.Statistical();
            //获取当前文件路径
            string filepath = Directory.GetCurrentDirectory();
            //定义文件输出路径
            filepath += path;
            FileInfo file = new FileInfo(@filepath);
            StreamWriter sw = file.AppendText();
            sw.WriteLine("characters: {0}", this.CharNum());
            sw.WriteLine("words: {0}", this.WordsNum());
            sw.WriteLine("lines: {0}", this.account);
            //遍历字典将内容写入文件
            foreach(KeyValuePair<string,int> kvp in this.words_sort)
            {
                sw.WriteLine("{0,-10}:{1,-3}", kvp.Key, kvp.Value);
            }
            //关闭文件
            sw.Close();
            Console.WriteLine("结果文件保存于:{0}", filepath);
        }
View Code

分析代码发现在ToFile()函数中进行了当前文件路径读取,读取文件,并多次写入文件(特别是遍历字典同时进行写入文件),由此可见循环写入文件是导致性能变差的原因。改进方法是将需要写入的内容保存至一个临时字符串中,遍历字典后再对整个字符串进行写入文件操作。ExtractChar()函数使用了正则进行匹配,由于对Regex类具体实现不清楚,暂时无法进行性能优化。(PS:在合作过程中,伙伴普及很多知识于本人,正则很耗费内存,还有封装,本人在重新加入-m功能的时候被伙伴吐槽,代码复用太多,难以封装)

 


 六、单元测试

 百度了下静态测试,前一次作业单元测试是无效的,通过伙伴的知识普及,了解到一个有效的单元测试是要有断言的。(PS:伙伴将封装后的代码文件发送给我,然后在自己电脑上测试并不能通过,因为电脑配置问题,还在其他同学的帮助下百度了问题。修改了配置文件,还是不行,最后用伙伴的电脑进行的测试)

 


七、异常处理

异常处理主要针对命令行传入参数的判定:

1.文件传入路径

//对得到内容进行处理 - i参数确定文件是否存在
            try
            {
                StreamReader sw = new StreamReader(command["-i"]);
            }
            catch
            {
                Console.WriteLine("需处理的文件不存在!请检查路径重新运行!");
            }

2.-m 与-n 参数内容是否为整数

//对-m -n 参数内容进行强制转换看是否为整数
if (command.ContainsKey("-n") && command.ContainsKey("-m"))
            {
                try
                {
                    int nums = Convert.ToInt32(command["-n"]);
                    int length = Convert.ToInt32(command["-m"]);
                }
                catch
                {
                    Console.WriteLine("参数输入有误,请重新运行!");
                }
            }
            else if (command.ContainsKey("-m") && !command.ContainsKey("-n"))
            {
                try
                {
                    int length = Convert.ToInt32(command["-m"]);
                }
                catch
                {
                    Console.WriteLine("参数输入有误,请重新运行!");
                }

            }
            else if (!command.ContainsKey("-m") && command.ContainsKey("-n"))
            {
                try
                {
                    int length = Convert.ToInt32(command["-n"]);
                }
                catch
                {
                    Console.WriteLine("参数输入有误,请重新运行!");
                }
            }
            else { }

八、结对过程

   在进行项目之前,和伙伴就约在了思学楼商讨了规则,当时没人给拍照,商讨了之后构建了PSP表格,进行编程。其实本人很清楚,此次编程中,伙伴花费了很多精力,最然本人也在参与,但是编程能力远远不如他,有趣的是在第一次商讨的时候我们在一起编写基础功能,然后他永远比本人快。(附上基础功能完成后,一起改进的照片)


 十、总结

本来和伙伴的编程能力就相差甚远,在这次编程中,个人是羞愧的,觉得1+1<2,没有自己的参与伙伴也许会更快的完成项目,在这次过程中于本人而言,1+1>2,因为伙伴普及了很多知识,包容我的过失等。为大学前两年没有好好学习感到懊悔,为自己拖后退感到抱歉,经过此次,我找到了好好学习的另一个理由,没有人为你的过失买单,想要好好的与人合作,更要提升自己,加油!期待下次和他合作的自己更好!

 

猜你喜欢

转载自www.cnblogs.com/Mchandu/p/10657935.html