合作——单丝不成线
Github项目地址:项目地址
结对伙伴的作业地址: 结对伙伴作业地址
作业地址:作业地址
代码规范
- 基本命名规范:
=》在命名时需要使用有意义的名称
=》优先使用英文,如果英文没有合适的单词,可以使用拼音,如城市名称等
=》禁止使用中文命名
=》命名不能使用缩写,如必须写成 person,不能写成 per
=》变量采用驼峰命名法
=》第一个单词以小写字母开始;从第二个单词开始以后的每个单词的首字母都采用大写字母,例如:myFirstName、myLastName。 - 布局规范:
=》使用 Tab 缩进,缩进大小为 4(Visual Studio 2017 中设置方法:菜单工具-选项-文本编辑器-C#-制表符,把制表符大小和缩进大小设置成4,选中“保留制表符”,点确定。)
=》左右花括号必须独占一行,括号内容为空时可在一行(Visual Studio 2017 中设置方法:菜单工具-选项-文本编辑器-C#-代码样式-格式设置-新行) - 注释:
=》对于复杂难理解的代码,要加上详细的注释,便于理解代码以及后续的代码复审。
PSP表格
PSP2.1 | Personal Software Process Stages | 预计耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 1200 | 1450 |
Estimate | 估计这个任务需要多长时间 | 1200 | 1450 |
Development | 开发(需求分析-具体编码) | 120+60*4+360=720 | 150+80*2+70+80+400=860 |
Analysis | 需求分析(包括新技术学习) | 120 | 150 |
Design Spec | 生成设计文档 | 60 | 80 |
Design Review | 设计复审(和同学审核设计文档) | 60 | 80 |
Coding Standard | 代码规范(为目前的开发制定规范) | 60 | 70 |
Design | 具体设计 | 60 | 80 |
Coding | 具体编码 | 360 | 400 |
Code Review | 代码复审 | 100 | 110 |
Test | 测试(自我测试、修改代码、代码提交) | 130 | 140 |
Reporting | 报告 | 100 | 120 |
Test Report | 测试报告 | 60 | 90 |
Size Measurement | 计算工作量 | 40 | 60 |
Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 50 | 70 |
total | 总计 | 1200 | 1450 |
代码思路分析
- 统计字符串中所有字符总数函数:用CharactersNum()函数,把文档中所有的字符串作为参数传入CharactersNum()函数,依次匹配字符串中的每个字符,对\s进行匹配,可利用 Matches()方法来逐个记录,最后返回字符串中所有字符总数
- 统计字符串中所有单词总数函数:需要利用集合的思想,在集合中保存满足条件的单词,即至少由4个字母组成的单词,利用正则,匹配[A-Za-z]{4}[A-Za-z0-9]*(\W|$,最后返回满足这些条件的集合,
- 统计字符串中文本行数函数:把文本内容即字符串作为函数的参数传入,对\r进行匹配,即换行符,利用正则的思想,Matches()中传入 \r,依次记录文本中 \r 的数量
- 统计一个集合中单词出现的频率函数:在统计完字符串中所有单词之后,再依次遍历集合中每个单词,把单词保存在集合中,匹配的过程中,如果出现相同 的单词,就把单词对应的数量+1,没有相同的,就先把单词放入到集合中,最后返回记录的单词以及该单词对应出现的次数的集合
- 找出单词字典中出现频率最高的n个单词函数:在完成统计一个集合中单词出现的功能之后,把返回值作为参数传入到该函数中,利用下标依次遍历,选择出符合条件的n个单词,并同时输出它们出现的次数
- 输出指定长度的单词词组(这里我们输出的词组中所有的单词的均满足要求:至少以4个英文字母开头):定义一个返回类型为字典的函数,接受已经筛选单个单词长度的返回结果List
集合和指定长度(WordNum)的参数,然后开始遍历接受的List 集合,把每WordNum个词组存到一个字典中。在把词组存入到字典之前,首先判断字典中是否存在该词组,若已经存在,则只把该词组的频率(该词组对应的值)+1,否则把该词组添加到字典中(同时,设定该词组的频率设为1)。 - 将字符串写入文件函数:这部分是参考CSDN上面的,利用FileStream()来new一个对象,指定输入的文件,利用StreamWriter()所创建的对象,来把运行结果写入文件
- 声明:初次拿到题目时,就考虑到应该用正则表达式来匹配,然后去找了C#关于正则的相关API,知道了使用方式,代码中用到了很多正则知识。对于在命令行运行程序,并通过命令行输入字符串作为参数传递给 program.cs 这个问题,百度搜索。
我负责的部分:基本功能中的统计文件字符数、统计单词数以及新增功能中的-m 参数设定统计的词组长度
代码复审
- 我们在各自完成了之前分配的函数编码后,开始代码互审。首先,我们根据之前制定的编码规范,审查对方的代码的可读性,有无模糊不清的函数名和变量名,注释是否清晰,代码可读性高不高;虽然,我们在编写代码的过程中都是按照我们之前制定的代码规范进行的,但是发现结对伙伴的个别变量名还是不够清晰,无法准确获取其所表达的意思,造成不必要的理解麻烦,增加了理解负担。然后,通过一些简单的测试检查对方的方法是否实现目标功能。
流程图
- 输出频率最高的num个单词:num为要输出的单词个数,dic.Count为要查找的字典的元素个数,x为临时变量
关键代码
- 根据要求,我们将统计字符串中所有单词的总数( WordsNum.cs )、找出单词字典中出现频率最高的n个单词( MaxFrequence.cs )、统计字符串中所有字符总数 ( CharactersNum.cs ),这三个函数独立封装了起来,其中 program.cs 为主函数, programTests.cs 为单元测试函数。
- 统计字符串中所有单词的总数的函数 WordsNum.cs
public class GetWordsNum
{
public static List<string> WordsNum(string text)
{
List<string> words = new List<string>();
MatchCollection matches = Regex.Matches(text, @"[A-Za-z]{4}[A-Za-z0-9]*(\W|$)"); // 利用正则实现匹配
foreach (Match match in matches)
{
words.Add(match.Value);
}
return words; // 返回一个储存了所有单词的集合(包括重复的单词)
}
}
- 找出单词字典中出现频率最高的n个单词 MaxFrequence.cs
public class GetMaxFrequence
{
/* 找出单词字典中出现频率最高的n个单词 */
public static Dictionary<string, int> MaxFrequence(Dictionary<string, int> dic, int n)
{
// dic储存单词及出现频率的字典 n为需要输出单词个数
Dictionary<string, int> d = new Dictionary<string, int>();
int x = 0;
// 判断需要的单词个数是否超出总单词个数
if (n <= dic.Count)
{
x = n;
}
else
{
x = dic.Count;
}
while (d.Count < x)
{
List<string> l = new List<string>(); // 多个单词有相同频率时储存至这个临时集合中按字典顺序排序
int maxValue = dic.Values.Max(); ; // 单词的最高频率
foreach (string s in dic.Keys)
{
if (dic[s] == maxValue) // 获取拥有最高频率的单词
{
l.Add(s); // 添加到临时集合中
}
}
foreach (string s in l) // 将单词从原来的字典中删除 准备下一次查找
{
dic.Remove(s);
}
l.Sort(string.CompareOrdinal);
foreach (string s in l)
{
d.Add(s, maxValue);
if (d.Count >= x) // 获取到10个单词后就退出
break;
}
}
return d;
}
}
- 统计一个集合中单词出现的频率的函数
public static Dictionary<string, int> WordFrequence ( List<string> wordList )
{
Dictionary<string, int> dictionary = new Dictionary<string, int>();
foreach (string word in wordList) // 遍历集合中每个单词
{
int value;
if (dictionary.TryGetValue(word, out value))
{
// 存在则将频率值 +1
dictionary[word] += 1;
}
else
{
// 不存在,则添加到集合中
dictionary.Add(word, 1);
}
}
return dictionary;
}
- 统计字符串中所有字符总数的函数 CharactersNum.cs
public static int CharactersNum(string text) // text为要统计的字符串
{
int num = 0;
num = Regex.Matches(text, @"[\S| ]").Count;
return num; // 返回字符总个数
}
- 按指定要求输出对应长度的词组函数 CountPhrase.cs
public class CountPhrase
{
// wordList 为已经筛选后的四个字母以上的单词集合,wordGroupNum为长度
public static Dictionary<string, int> WordsGroups(List<string> wordList, int wordGroupNum)
{
// 创建字典用于保存 -m 功能的字符数组
Dictionary<string, int> wordsGroup = new Dictionary<string, int>();
int i;
int j = 0;
// 循环判断 -m 后输入的数字,截取对应长度的词组
for (i = 0; i <= wordList.Count - wordGroupNum; i++)
{
// str 用来临时存储当前单词个数为 wordGroupNum 的词组
string str = "";
for (j = i; j < wordGroupNum + i; j++)
{
str += wordList[j];
}
// 对词组频率进行统计
if (wordsGroup.ContainsKey(str))
{
// 如果词组已经存在,则频率 +1
wordsGroup[str] += 1;
}
else
{
// 否则加入新的字典中
wordsGroup.Add(str, 1);
}
}
return wordsGroup;
}
}
异常处理
关于文件读取的异常处理
- 刚开始没有留意到这一点,知道后来有一次输入时不小心输错了,才发现了问题。
- 然后做了相应的修改:加入文件路径存在问题,则显示“文件路径不正确”,并提示“检查,重新输入”。
代码实现过程(测试、分析)
- 测试计算字符串中字符总数函数
- 测试计算字符串中行数总数函数(主要针对 \r )
- 测试计算字符串中、出现频率最高的10个单词总数函数
- 可点击【此结果的其他输出】 ,来查看测试结果,点击如下图:
- 测试按指定要求输出对应长度的词组函数
- 测试完成
- 初步性能分析图
- 详细性能分析
-
多个测试用例
-
运行效果
=》待检测的文件 input.txt:
=》命令行输入方式:
=》命令行输入方式以及对应结果
=》命令行输入方式以及对应结果
总结
- 这一次的结对编程作业对于我一个人而言,还是具有很大难度的,如果要我自己一个人独立完成这次作业的话,肯定要花费比现在更多的时间以及精力,可能有些方法也实现不了。通过这一次的作业,让我切实地感受到了1+1>2的力量,一个人或许可以走得很快,但是一群人可以走得更远,这就是团队的力量。在完成的作业过程中,遇到的问题不计其数,但是两个人协力解决还是可以节省很多时间的,每个人的知识储备也不同,针对同一个问题就有了更多的解决办法。总而言之,这次的作业过程充满了辛酸泪,一路坎坷与泥泞,但是让我明白自己知识不扎实、能力不足(没错,这才是重点),让我再一次深省了自己。