作业四结对编程
Git地址 | https://github.com/isliudong/WordCount |
---|---|
结对同学的作业地址 | <同伴博客> |
结对同学的学号 | 201731062214 |
个人博客地址 | https://www.cnblogs.com/liudong123/ |
作业要求 | <作业要求> |
一、结对过程(拍照)
二、PSP表格
PSP2.1 | Personal Software Process Stages | 预计耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 20 |
Estimate | 估计这个任务需要多长时间 | 30 | 20 |
Development | 开发(需求分析-具体编码) | 400 | 450 |
Analysis | 需求分析(包括新技术学习) | 200 | 200 |
Design Spec | 生成设计文档 | 20 | 30 |
Design Review | 设计复审(和同学审核设计文档) | 30 | 40 |
Coding Standard | 代码规范(为目前的开发制定规范) | 20 | 15 |
Design | 具体设计 | 60 | 80 |
Coding | 具体编码 | 200 | 200 |
Code Review | 代码复审 | 60 | 60 |
Test | 测试(自我测试、修改代码、代码提交) | 120 | 120 |
Reporting | 报告 | 80 | 60 |
Test Report | 测试报告 | 45 | 40 |
Size Measurement | 计算工作量 | 20 | 25 |
Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 20 | 15 |
total | 总计 | 1335 | 1375 |
三、基本解题思路
1.当我们看到本篇文章的时候,大概看了一下需求,是一个类似wordCount的程序,用来统计TXT文档的一些内容。
2.读取需要处理的文本到,然后用split和正则表达式拆分成数组,然后所有的按照要求进行设置,对数组中的元素,按照博客作业地址给的规范,挨个实现功能
3.考虑到后续可能会增加一些新的功能,要提前留出一些接口来实现功能可以实现
4.确立首先采用命令行编程,然后再过渡为面向对象编程
四、程序设计(详细)
根据我们首先得命令行变成实现之后,我们打算重构代码,实现代码解耦还有面向对象编程,本次实验大致有下面几个功能统计字符,统计单词总数,统计行数
还有每个单词出现的次数于是设计了几个类:
Do类:
Compare类:
Program类:
Read类:
Text类:
Write类:
- 逻辑流程设计
- 接口封装设计
-
命令行参数设计
在cmd命令行输入的参数会存入程序入口主函数main(string[] args)的字符串数组中。//对-l、-m、-n、-o参数识别并保存他们后面的输入值 for(int i=0;i<args.Length;i++) { if (args[i] == "-l")//路径参数 { path = args[i + 1]; i++; } else if (args[i] == "-m")//参数设定统计的词组长度 { wordlength = int.Parse(args[i+1]); i++; } else if (args[i] == "-n")//参数设定输出的单词数量 { outnum = int.Parse(args[i + 1]); i++; } else if (args[i] == "-o")//参数设定生成文件的存储路径 { outpath = args[i + 1]; i++; } }
-
词组个数设计实现(-m)
//嵌套循环生成词组 words[0] = word[1]; for(int i=0;i<word.Length-wordlenth;i++) { for (int j = i; j <= i+wordlenth-1; j++) { words[i] = words[i] + " "+word[j]; } }
-
按照参数输出高频次数个数(-n)
在Do类中通过调用Compare类实现单词比较,在通过词频优先进行排序,输出前n个词组。//单词比较算法 public int compare(String str1, String str2) { int length1 = str1.Length; int length2 = str2.Length; int limit = Math.Min(length1, length2); char[] a = str1.ToCharArray(); char[] b = str2.ToCharArray(); for (int i = 0; i < limit; i++) { char c1 = (char)(a[i] >= 'a' ? a[i] : (a[i] + 32)); char c2 = (char)(b[i] >= 'a' ? b[i] : (b[i] + 32)); if (c1 != c2) { return c1 - c2; } } return length1 - length2; }
五、代码规范
=》在命名的时候使用有意义的名称
=》禁止使用中文命名
=》变量采用驼峰命名法
=》对于一些复杂的功能和代码,加上详细的注释
=》采用缩进换行等方式,使得代码看起来整洁规范
六、代码互审
1.在代码复审的时候,我们发现命名有几个没有按照要求来命名,于是我们更正了命名
2.在功能实现的时候我们发现了一些小的细节问题,那就是把空格字符当成了单词输入,导致最终统计的数量不实际数量多,我把这个地方重新设计了一些,更改了一下判断的条件
3.还有一些小bug没来得及修复,但是不影响实际的操作(滑稽)
七、单元测试及异常处理
单元测试:
-
对单词字典比较算进行测试(Compare.compare()):
public class CompareTests { [TestMethod()] public void compareTest() { Compare compare = new Compare(); String[] word1 = { "as", "ae", "th", "cpig" }; String[] word2 = { "we", "are", "the", "pig" }; int t; bool k=false; for (int i = 0; i < 4; i++) { t = compare.compare(word1[i], word2[i]); if (t < 0) { k = true; } Assert.AreEqual(true,k ); } } }
-
测试结果:
-
复制字符混合测试
测试文本内容
测试成功
异常处理:
-
对文件读入进行测试
-
测试代码设计:
public void ReadTextTest() { Read read = new Read(); read.ReadText("D:\a.txt", 1); for(int i=0;i<read.word.Length;i++) { Assert.AreNotEqual("", read.word[i]); } //Assert.Fail(); }
-
运行出错:
-
发现是由于word = Regex.Split(sr.ReadToEnd(), @"\W+");代码出现问题,split函数通过正则表达式拆分出来单词数组存在首尾为空的情况。
通过将数组存入hashtable时进行非空判断,解决问题如图所示。
-
写文件文档路径是否存在异常处理:
-
代码设计:
FileStream fileStream1 = new FileStream(l, FileMode.OpenOrCreate, FileAccess.ReadWrite); fileStream1.Close();//如果文件不存在会自动在该路径下创建写入文件
-
-
测试心得:
1、测试需要多参数测试,尽量覆盖用户可能的输入类型
2、有些函数是被调用函数,测试不成功但是在调用者函数里进行处理,也是可以的。
3、测试尽量覆盖所有核心代码
八、性能测试及改进
改进花费时间:30mins
-
性能测试:
-
函数性能分析:
主要是hashtable的创建,字母的比较和读入字符三个部分吃性能
改进方案:
将hashtable的写入算法优化,不在重复判断是否包含value,而是在这之前就对数组进行消重操作。这将很好的提高hashtable部分的性能。
九、代码展示及程序运行结果截图
-
Main:
public static void Main(string[] args) { int wordlength=1; int outnum=0; string outpath="/"; string path=null; for(int i=0;i<args.Length;i++) { if (args[i] == "-i")//路径参数 { path = args[i + 1]; i++; } else if (args[i] == "-m")//参数设定统计的词组长度 { wordlength = int.Parse(args[i+1]); i++; } else if (args[i] == "-n")//参数设定输出的单词数量 { outnum = int.Parse(args[i + 1]); i++; } else if (args[i] == "-o")//参数设定生成文件的存储路径 { outpath = args[i + 1]; i++; } } new Do().doing(path, wordlength, outnum, outpath); }
-
ReadText:
public String ReadText(String path,int wordlenth) { StreamReader sr = new StreamReader(path, Encoding.Default); while(sr.Read()!=-1) { sum++; } row= sr.ReadToEnd().Split('\n').Length; sr.BaseStream.Seek(0, SeekOrigin.Begin);//重置流指针 row = sr.ReadToEnd().Split('\n').Length;//行数统计 sr.BaseStream.Seek(0, SeekOrigin.Begin); word = Regex.Split(sr.ReadToEnd(), @"\W+");// words = new string[word.Length-wordlenth]; words[0] = word[1]; for(int i=0;i<word.Length-wordlenth;i++) { for (int j = i; j <= i+wordlenth-1; j++) { words[i] = words[i] + " "+word[j]; } } sr.BaseStream.Seek(0, SeekOrigin.Begin);//重置流指针 return sr.ReadToEnd(); }
-
写入文件:
using (StreamWriter sw = new StreamWriter(outpath)) { sw.WriteLine("单词数:" + count);//单词数 sw.WriteLine("字符数:" + zifushu); sw.WriteLine("行数:" + hangshu); sw.WriteLine("词汇量:" + cihui); sw.WriteLine("词组频统计(词频优先字典序):"); for (int i = 0; i < wd.Length; i++) { sw.WriteLine(wd[i] + ": " + hashtable[wd[i]]); } sw.Close(); Console.ReadLine(); }
-
将词组存入hashtable:
for (int i = 0; i < read.words.Length; i++) { if (hashtable.ContainsKey(read.words[i])) { geshu = (int)hashtable[read.words[i]]; geshu++; hashtable[read.words[i]] = geshu; } else { if (read.words[i] != "")//取出split产生的空字符 hashtable.Add(read.words[i], times[i]); } }
-
命令行运行结果:
十、心得总结
结对编程可以让产生1+1>2的效果,但是效率上需要队友很好的配合,不让可能造成1+1<1的后果,只要配合好就能实现1+1>2,经过这次合作项目,我体会到了做好规划的重要性。最开始随便写了算法代码也比较乱导致后面新需求出现的时候极难改进。所以细节是很重要的,特别是对编程来说。