一、开头
博客作业地址 | 链接 |
---|---|
Github地址 | 链接 |
同伴博客 | 链接 |
二、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 30 |
· Estimate | · 估计这个任务需要多少时间 | 800 | 800 |
Development | 开发 | 300 | 400 |
· Analysis | · 需求分析 (包括学习新技术) | 120 | 200 |
· Design Spec | · 生成设计文档 | 30 | 30 |
· Design Review | · 设计复审 (和同事审核设计文档) | 20 | 30 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 10 |
· Design | · 具体设计 | 30 | 20 |
· Coding | · 具体编码 | 300 | 500 |
· Code Review | · 代码复审 | 30 | 60 |
· Test | · 测试(自我测试,修改代码,提交修改) | 100 | 20 |
Reporting | 报告 | 60 | 30 |
· Test Report | · 测试报告 | 30 | 30 |
· Size Measurement | · 计算工作量 | 20 | 20 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 1080 | 1410 |
三、计算模块接口的设计与实现过程
接口的设计
首先拿到一个题目当然是要先理清题目的意思啦,这次题目的要求是设计统计一个文件中的几项指标,其中包括:
-
统计文件的字符数
-
输出的单词统一为小写格式
-
统计文件的单词总数,单词:至少以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写。
-
统计文件的有效行数:任何包含非空白字符的行,都需要统计。
-
统计文件中各单词的出现次数,最终只输出频率最高的10个。频率相同的单词,优先输出字典序靠前的单词。
-
按照字典序输出到文件txt:例如,windows95,windows98和windows2000同时出现时,则先输出windows2000
在统计过程中,对字符数是有要求的,要求如下:
- 只需要统计Ascii码,汉字不需考虑
- 空格,水平制表符,换行符,均算字符
- 英文字母:A-Z,a-z
- 字母数字符号:A-Z,a-z,0-9
- 分割符:空格,非字母数字符号
- 例:file123是一个单词,123file不是一个单词。file,File和FILE是同一个单词
- 输出的单词统一为小写格式
这次的项目对我们来说是一种挑战,因为即使是在第一次作业中使用过了c#进行一些编程,但是在那次项目中我感觉自己还是在“面向过程编程”,在这次中希望自己能好好提升,增加对“面向对象的理解”。
在这次设计中,我和我的同伴经过讨论,决定将每一个功能拆分成一个类,最后在主函数中调用。这样的话不仅便于我们分工协作,同时也让代码更加具有独立性。
类的结构图设计:
流程图设计
具体类:
算法关键:
这次的项目中运用到算法的部分不是很多,我们主要集中在对单词的频率排序部分,以及判断是否满足条件部分使用。
在这次项目中,我们运用了大量正则表达式,正则表达式有以下优点:
1、灵活性、逻辑性和功能性非常的强;2、可以迅速地用极简单的方式达到字符串的复杂控制;
在这次项目中,运用正则表达式极大的优化了我们的时间和精力;
如何体现“Design by Contract”、“Information Hiding”、 “Interface Design”、 “Loose Coupling”等原则
Information Hiding:信息隐藏,在这次项目中我们体现在将数值用变量代替,很好的保护了信息;
Interface Design:接口设计有六大原则,在这次项目中因为自己的代码还不够成熟,我们主要强调在接口名字规范,接口之间独立。
Loose Coupling:低耦合度,我们这次体现在增加接口上。
实现过程;
首先是基础功能的实现:
代码展示
//打开文件并统计字符个数部分类
class asccount
{
//打开文件并统计字符个数
public static int agefile()
{
int num = 0;
try
{
string str = File.ReadAllText(@path.s);
num = Regex.Matches(str, @".").Count;
num = num + linescount.lines() - 1;
}
catch (Exception e)
{
Console.WriteLine("请输入正确的文件路径");
}
return num;
}
}
//读取行数类
class linescount
{
public static int lines()//统计文件中的行数
{
string str = File.ReadAllText(@path.s);
int nr = Regex.Matches(str, @"\r").Count + 1;
return nr;
}
}
//统计字频数类
class wordcount
{
public static int sum1(Dictionary<string, int> dic1Asc)
{
//将每个单词按字典顺序写入文件中
string outpath = "";
try
{
outpath = @"D:\xe.txt";
File.Exists(outpath);
}
catch (Exception e)
{
Console.WriteLine("无法找到改文件");
}
//string outpath = @"C:\Users\李星晨\Desktop\output.txt";
StreamWriter sw1 = new StreamWriter(outpath);
int sum = 0;
foreach (KeyValuePair<string, int> entry in dic1Asc)
{
string word = entry.Key;
int frequency = entry.Value;
sw1.Write(word + "\r\n");
sum = sum + frequency;
}
sw1.Close();
return sum;
}
//统计字频数
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 j = 0;
//判断是否是单词
string[] newwords = ynword.ynword1(words,ref j);
string[] newwords1 = new string[j];
for (int i = 0; i < j; i++)
{
newwords1[i] = newwords[i];
}
foreach (string word in newwords1)
{
if (frequencies.ContainsKey(word))
{
frequencies[word]++;
}
else
{
frequencies[word] = 1;
}
}
return frequencies;
}
}
//判断是否符合题目要求类
class ynword
{
public static string[] ynword1(string[] words,ref int w)
{
char[] newword = new char[words.Length];
int k = 0;
string[] newwords = new string[words.Length];
for (int i = 0; i < words.Length; i++)
{
char[] wordss = words[i].ToCharArray();
for (int j = 0; j < wordss.Length; j++)
{
if (wordss[j] >= 'A' && wordss[j] <= 'Z')
{
wordss[j] = Convert.ToChar((Convert.ToInt32(wordss[j]) + 32));
}
}
if (wordss.Length >= 4)
{
if ((wordss[0] >= 'a' && wordss[0] <= 'z') && (wordss[1] >= 'a' && wordss[1] <= 'z') && (wordss[2] >= 'a' && wordss[2] <= 'z') && (wordss[3] >= 'a' && wordss[3] <= 'z'))
{
newword = wordss;
string s = String.Join("", newword);
newwords[k] = s;
k++;
}
}
}
w = k;
return newwords;
}
}
展示部分代码,因为在git上有运行过程就不展示全部代码了,下面展示运行结果:
vs中运行结果:
输出到文件中的运行结果,按字典顺序对单词进行排序:
这一点我看好多人都是都是把输出结果保存在文件中,我和我的同伴商量后还是觉得题目的要求是按字典顺序对单词进行排序,所以结果如下:
新加功能实现过程
新加功能时在命令行中输入参数并执行,这一点有很多解决方法,我选择的下面你这种,是我百度最简单方便的一种啦:
static void Main(string[] args)
{
string s = "";
int m = 0;
int n = 0;
//设置默认文件夹
string outpath = @"D:\xe.txt";
for (int i = 0; i < args.Length; i++)
{
switch (args[i])
{
//输出几个高频词
case "-m":
m = int.Parse(args[i + 1]);
break;
//输入文件路径
case "-i":
s = args[i + 1];
break;
//输出文件路径
case "-o":
outpath = args[i + 1];
break;
//输出n个单词的个数
case "-n":
n = int.Parse(args[i + 1]);
break;
}
}
执行结果如下图:
运行的时候是将每一种参数都进行过尝试的,这里展示两张图:
- 读取文件,写入文件:
- 使用-m -n参数
四、代码复审过程
代码规范
首先得有代码规范,才会有代码复审。
代码规范分两个:
1.是代码风格规范。主要是文字是的规定。
2.是代码设计规范,牵涉带程序设计。模块之间的关系。
在这次项目前,我和我的同伴主要考虑是代码风格规划,虽然只有两个人,但是是非常重要的部分;
代码风格的原则是:简明,易读,无二义性。既然只有两个人,我们便两个一起制定了符合两个习惯,着重将两个人习惯不一样的制定了标准。
标准如下:
- 缩进:tab键;
- 行宽不得大于100字符;
- 不要把多条语句放在一行上;
- 命令用小驼峰;
- 需要写注释
代码复审
代码复审我认为是结队编程中最重要的一步,结队编程最后的结果取决了能力较高的那个人,我觉得也是在这一步中体现出来嘚;
代码复审我们采用先自审再复审,复审阶段我们考虑到对方平常生活中都是不拘小节的人,所以担心对方错过很多需要审查的地方,所以我们做了一个核查表,一式两份,站在同等标准上去审查对方的代码:
审查目的 | 结果 |
---|---|
代码是否有编译错误 | 无 |
代码是否符合代码规范 | 大部分符合 |
是否有逻辑、算法错误 | 无 |
设计考虑是否周全 | 有些地方不够周全 |
代码可读性如何 | 一般,注释较少 |
代码容易维护吗 | 较容易 |
是否有异常处理 | 有 |
是否有无用代码 | 有 |
通过表格我们就可以很清楚的了解到审查结果啦,比如我们对一些无用的代码进行了改进,加快了运行效率。
通过代码复审,我发现了很多自己的问题,比如在命令上面,因为总是自己写代码所以会有拼音和英文夹杂的情况,自己一直没太在意过,这次经过同伴的提点发现是一个非常不好的习惯,这样别人看自己的代码的时候会很费力。
还有就是注释部分,这次代码的主要设计者是我的同伴,但是她的代码注释极少,在这点上非常不符合代码规范,我们一起增加了注释。
代码审查教会我站在别人的角度看代码,对我提神很大;
五、性能改进
在代码互审后,我们的代码有了很大的提升。这时候进行性能分析:
CPU使用率:
性能报告
可以看到我们主函数中占的CPU较多,但是也在接受范围内;
六、单元测试
这一次是单元测试我们是将所有类都进行了测试,但是因为有点类的参数依赖于另一个类,所以在测试时我们对代码进行了微小的改变,便于测试。
我们这一次已经进行了三次测试,路径如下:
第一次测试
文件图:
先测试简单的,文件中字符数6个,单词1个,行数为2;
字符数的测试截图:
行数的测试截图:
可是看到都是测试通过的。
七、异常处理部分
我和我的同伴认为在这次项目中,输入输入文件的异常处理是最重要的;
输入文件:
try
{
string str = File.ReadAllText(@path.s);
num = Regex.Matches(str, @".").Count;
num = num + linescount.lines() - 1;
}
catch (Exception e)
{
Console.WriteLine("请输入正确的文件路径");
}
输出文件:
string outpath = "";
try
{
outpath = @"D:\xe.txt";
File.Exists(outpath);
}
catch (Exception e)
{
Console.WriteLine("无法找到改文件");
}
八、结对过程
一拍即合,就结对啦。
九、附加功能
这部分因为时间的关系是没能完成的,只完成了部分,贴张图意思意思。
十、总结和收获
这次项目真的挺难的,c#感觉真的噩梦,虽然之前很简单了解过但是代码能力真的挺差的,而且一直感觉掌握不了c#的真谛。
而且这次结对过程也是困难满满,一开始我们还是想以前一样分工,但是这次作业是一步扣一步,上一步没完成没法进行下一步,所以我们后期都是一起编程,就像书上说的,结对编程就像跳舞,一开始两个人确实是不适应的 ,但是慢慢的就会好起来。而且后期很多工作,如单元测试、性能分析都是一起做的,效率更高,同时也更有趣。
我认为像两个人分工写代码,确实有着一起写来的方便。一起写不懂就问,而且一起监督,事半功倍。我记得有一个下午我看我同伴的代码,半天也没捣鼓出来,但是第二天我去问她,十分钟就解决了疑惑。
所以我认为结对编程时1+1>2的,但是是否一直大于2,我觉得不是,如果是两个不熟悉的人,要花大量的时间去熟悉对方,我觉得是得不偿失的。
(ps:说出来可能没人相信,截止日期前三个小时的时候,我的同伴来看我的博客,告诉我我把团队作业粘贴到个人作业提交,我当时觉得没事,不就是重新复制粘贴一次吗,当我打开电脑的时候,我的文档存档它....不知道为什么...只保存到了第三点....,我当时真的觉得人生太艰难了,然后又重新写了一次,这次的作业真的太痛苦了o(╥﹏╥)o)