GIT地址 | https://github.com/Chengxiaoxi-299 |
GIT用户名 | Chengxiaoxi-299 |
结对伙伴博客地址 | https://www.cnblogs.com/SabrinaL/ |
博客地址 | https://www.cnblogs.com/chengxiao299/ |
作业链接 | https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass1/homework/2882 |
1、Fork仓库的Github项目地址:
https://github.com/Chengxiaoxi-299/WordCount.git
2、预估各个模块开发耗费的时间:
PSP2.1 | PersonalSoftware Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 15 |
-Estimate | -估计这个任务需要多少时间 | 30 | 20 |
Development | 开发 | 600 | 630 |
-Analysis | -需求分析(包括学习新技术) | 120 | 60 |
-Design Spec | -生成设计文档 | 20 | 25 |
-Design Review | -设计复审(和同事审核设计文档) | 20 | 10 |
-Coding Standard | -代码规范(为目前的开发制定合适的规范) | 10 | 10 |
-Design | -具体设计 | 25 | 25 |
-Coding | -具体编码 | 120 | 150 |
-Test | -测试(自我测试,修改代码,提交修改) | 30 | 40 |
Reporting | 报告 | 30 | 45 |
-Test Report | -测试报告 | 20 | 20 |
-Size Measurement | -计算工作量 | 20 | 15 |
-Postmortem&Process Improvement Plan | -事后分析,并提出过程改进计划 | 30 | 30 |
- | 合计 | 1105 | 1095 |
3、结对过程
实际操作过程中,分工和预想的并不完全一样。我们先确定了具体的功能需求,再分别完成各自相应负责的部分。
结对编程可以在交流中发散思维,能够很快的设计出具体的程序框架;同时可以很大程度上提高效率,减少代码复审率。但由于个人风格不同,导致又在结对编程之前花费时间规定代码风格。有时会因为讨论的不及时或者表达不清晰,写出无用的代码,不及单人编程的效果。
4、解题思路
通过阅读和理解后,我们认为项目需求可分为两个部分:基本功能和扩展功能。核心问题是字符串的统计(长度、出现频率)及排序。
- 首先想到采用C#编程可以使用Dictionary字典类及其相关函数来实现排序的相关功能;
- 而对于正确格式的单词可通过使用正则表达式来进行判断;
- 扩展功能只需要在完成基础功能的基础之上加入相应的命令判断函数即可实现
- 完成基础功能后就可以封装功能函数,进行相应的测试
5、代码设计
根据项目需求主要设计了六个功能类:WordNumber、CharNumber、Frequencies、_Sort、_LimitOut、Statistic
程序使用流程
CharNumber类
WordNumber类
Frequencies类
_Sort类
Statistic类
_LimitOut类
6、性能分析图
7、主要代码
CharNumber类
public class CharNumber { public static void charNumber(string in_Path,string out_Path) { int countChar = 0; int countLine = 0; int[] count = new int[2];//统计字符数&行数 try { FileStream file = new FileStream(in_Path, FileMode.Open); StreamReader sr = new StreamReader(file); string readLine = null; while ((readLine = sr.ReadLine()) != null) { countChar += readLine.Length; countLine++; } count[0] = countChar; count[1] = countLine; sr.Close(); } catch(Exception e) { Console.WriteLine(e.Message.ToString()); } //写入操作 try { FileStream wFile = new FileStream(out_Path, FileMode.Create); StreamWriter sw = new StreamWriter(wFile); sw.WriteLine("characters: {0}", count[0]); sw.WriteLine("lines: {0}", count[1]); sw.Close(); } catch (Exception e) { Console.WriteLine(e.Message.ToString()); } } }
WordNumber类
public class WordNumber { public static void wordNumber(string in_Path, string out_Path) { try { FileStream file = new FileStream(in_Path, FileMode.Open); StreamReader sr = new StreamReader(file); List<string> WordsList = new List<string>();//储存处理后的单词 int countWords = 0; //输入统计字符串 //string wholeText = Console.ReadLine(); //定义分割符 Regex regex = new Regex("^[a-zA-Z]{4,}[a-zA-Z0-9]*"); string readLine = null; while ((readLine = sr.ReadLine()) != null) { string[] wordsArr1 = Regex.Split(readLine, "\s*[^0-9a-zA-Z]+");//以空格和非字母数字符号分割,至少4个英文字母开头,跟上字母数字符号 foreach (string word in wordsArr1) { //word = word.ToLower(); if (regex.IsMatch(word.ToLower())) { if (WordsList.IndexOf(word.ToLower()) == -1) //判断大小写不同的重复单词 { WordsList.Add(word); countWords++; } } } } sr.Close(); //写入操作 FileStream wFile = new FileStream(out_Path, FileMode.Append); StreamWriter sw = new StreamWriter(wFile); sw.WriteLine("words: {0}", countWords); sw.Close(); } catch(Exception e) { Console.WriteLine(e.Message.ToString()); } } }
Frequencies类
public class Frequencies { public static Dictionary<string, int> frequencies(string Path) { try { FileStream file = new FileStream(Path, FileMode.Open); StreamReader sr = new StreamReader(file); Regex regex = new Regex("^[a-zA-Z]{4,}[a-zA-Z0-9]*"); Dictionary<string, int> frequencies = new Dictionary<string, int>(); //建立字典 string readLine = null; while ((readLine = sr.ReadLine()) != null) { string[] wordsArr1 = Regex.Split(readLine, "\s*[^0-9a-zA-Z]+");//以空格和非字母数字符号分割,至少4个英文字母开头,跟上字母数字符号 foreach (string word in wordsArr1) { if (regex.IsMatch(word.ToLower())) { //统计词频 if (frequencies.ContainsKey(word.ToLower())) { frequencies[word.ToLower()]++; } else { frequencies[word.ToLower()] = 1; } } } } sr.Close(); return frequencies; } catch(Exception e) { Console.WriteLine(e.Message.ToString()); } return null; } }
_Sort类
public class sort { public static void Sort(Dictionary<string, int> dic, string out_Path) { try { //同频率按字典序排序 //先按照key值字典序升序排序,再按照value值降序排序 var _dicSort = from objDic in dic orderby objDic.Key select objDic; var dicSort = from objDic in _dicSort orderby objDic.Value descending select objDic; string[] str = new string[dicSort.Count()]; FileStream wFile = new FileStream(out_Path, FileMode.Append); StreamWriter sw = new StreamWriter(wFile); if (dicSort.Count() < 10) { foreach (KeyValuePair<string, int> kvp in dicSort) { sw.WriteLine("<" + kvp.Key + ">:" + kvp.Value); } } else { for (int i = 0; i < dicSort.Count(); i++) { foreach (KeyValuePair<string, int> kvp in dicSort) { sw.WriteLine("<" + kvp.Key + ">:" + kvp.Value); } } } sw.Close(); } catch(Exception e) { Console.WriteLine(e.Message.ToString()); } } }
Statistic类
public class Statistic { public static void statistic(Dictionary<string, int> dic, string Path, int num) { try { FileStream wFile = new FileStream(Path, FileMode.Append); StreamWriter sw = new StreamWriter(wFile); Dictionary<KeyValuePair<string, int>, int> st = new Dictionary<KeyValuePair<string, int>, int>(); int flag = 0; foreach (KeyValuePair<string, int> kvp in dic) { int len = kvp.Key.Length; st.Add(kvp, len); } foreach (KeyValuePair<KeyValuePair<string, int>, int> kvp in st) { if (kvp.Value == num) { flag = 1; sw.WriteLine("The length of word is " + num + " : <" + kvp.Key.Key + ">:" + kvp.Key.Value); } } if (flag == 0) { sw.WriteLine("Nothing"); } sw.Close(); } catch (Exception e) { Console.WriteLine(e.Message.ToString()); } } }
limitOut类
public class limitOut { public static void LimitOut(Dictionary<string, int> dic, int i, string Path) { try { FileStream wFile = new FileStream(Path, FileMode.Append); StreamWriter sw = new StreamWriter(wFile); foreach (KeyValuePair<string, int> kvp in dic) { if (i > 0) { sw.WriteLine("<" + kvp.Key + ">:" + kvp.Value); i--; } } sw.Close(); } catch(Exception e) { Console.WriteLine(e.Message.ToString()); } } }
主函数
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Text.RegularExpressions; using System.IO; using Char_Number; using _Frequencies; using Word_Number; using _Sort; using _Statistic; using _LimitOut; namespace ConsoleApplication9 { class Program { static void Main(string[] args) { //默认参数 string in_mess = @"G:input.txt"; string out_mess = @"G:output.txt"; int wordLength = 2; int wordCount = 3; if (args.Length > 0) // 判断输入参数 { switch (args[0]) { case "-i": in_mess = args[1]; break; case "-m": wordLength = int.Parse(args[1]); break; case "-n": wordCount = int.Parse(args[1]); break; case "-o": out_mess = args[1]; break; } if (args.Length > 2) switch (args[2]) { case "-i": in_mess = args[3]; break; case "-m": wordLength = int.Parse(args[3]); break; case "-n": wordCount = int.Parse(args[3]); break; case "-o": out_mess = args[3]; break; } if (args.Length > 4) switch (args[4]) { case "-i": in_mess = args[5]; break; case "-m": wordLength = int.Parse(args[5]); break; case "-n": wordCount = int.Parse(args[5]); break; case "-o": out_mess = args[5]; break; } if (args.Length > 6) switch (args[6]) { case "-i": in_mess = args[7]; break; case "-m": wordLength = int.Parse(args[7]); break; case "-n": wordCount = int.Parse(args[7]); break; case "-o": out_mess = args[7]; break; } } CharNumber.charNumber(in_mess, out_mess);//统计字符数&行数 WordNumber.wordNumber(in_mess, out_mess);//统计单词数 Dictionary<string, int> frequencies = Frequencies.frequencies(in_mess);//统计词频 sort.Sort(frequencies, out_mess);//排序 if (wordLength != 0) { Statistic.statistic(frequencies, out_mess, wordLength); } if (wordCount != 0) { limitOut.LimitOut(frequencies, wordCount, out_mess); } } } }
8、代码复审
- 在进行主要功能函数封装之前,每完成一个模块的功能函数就互相进行一次代码互审
- 确认逻辑无误,能够顺利运行后进行单元测试
举例:wordNum类 Frequencies类
public void wordNumberTest() { string in_mess = @"G:input.txt"; string out_mess = @"G:output.txt"; int num = 64; int _num = WordNumber.wordNumber(in_mess, out_mess);//函数输出结果为单词数 Assert.AreEqual(num, _num);//判断统计单词数是否相等 //Assert.Fail(); }
public void sortTest() { //建立两个同内容不同单词顺序的文本 string mess1 = @"G:input.txt"; string mess2 = @"G:put.txt";
//比较建立的按统计词频排序字典是否相同 Dictionary<string, int> dic1 = sort.Sort(mess1); Dictionary<string, int> dic2 = sort.Sort(mess2); Assert.AreEqual(dic1 , dic2); //Assert.Fail(); }
9、项目总结
第一次体验结对编程,在以前的项目中体验过合作编程,但不同的是代码互审环节。本次结对编程的效率比个人编程的效率要高出很多,此种方式提高了开发速度,降低了出错率,实时沟通降低了后期交流的成本。
结对编程实现1+1>2的重要途径就是统一代码规范,避免出现看不懂对方所写代码,或者误解、不解调用方法。
在本次项目实战中,深刻体会到代码可读性的重要性,积累了合作编程的经验。