一、合作 :
github项目地址:https://github.com/Shen-MoMo/WordCount
合作同学作业地址:https://www.cnblogs.com/sm644245985/p/11670233.html
结对成员学号:201731062502 ,201731062115
二、PSP表格:
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
|
|
· Estimate |
· 估计这个任务需要多少时间 |
30 |
30 |
Development |
开发 |
|
|
· Analysis |
· 需求分析 (包括学习新技术) |
60 |
60 |
· Design Spec |
· 生成设计文档 |
30 |
30 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
30 |
30 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
10 |
10 |
· Design |
· 具体设计 |
30 |
120 |
· Coding |
· 具体编码 |
60 |
120 |
· Code Review |
· 代码复审 |
30 |
90 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
60 |
120 |
Reporting |
报告 |
|
|
· Test Report |
· 测试报告 |
30 |
30 |
· Size Measurement |
· 计算工作量 |
30 |
30 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
30 |
30 |
|
合计 |
430 |
700 |
PSP表格在记录项目完成进度上很有帮助,可以让成员清晰的看到进度与每个进程的花费时间,在项目开始之前,预估时间也是对本次此项目的一个时间安排,但是往往预估的时间都比我们实际话费的时间时间更少。
三、计算模块接口的设计与实现过程:
1、项目需求:
- 统计字符数
- 统计单词数
- 统计行数
- 统计结果以指定格式输出到默认文件中
- 其他扩展功能
2、我的思路:
(我只做了两个功能,搭档做了另外两个)
- 首先对文件内容的提取,采用C#提供的库函数:FileStream,使用时注意文件路径编写
- 然后我负责完成统计文件中的字符数字个数,和文件行数,
- 在编写getChacactor()函数时,得到文件内容,通过ReadLine()方法将文件内容读取并存入字符串中,然后利用ascii码值在0-127之间的作为字符,这样排除了中文,设置count变量,如果他的ascii在0-127之间就复合要求
- 在编写getRows()函数时,用文件流StreamReader方式读取文件,保存读取内容,定义‘\n’为跳过标志,用Split()对保存内容按跳过的字符类型,分割成字符串,最后统计这个字符串的长度
3、流程图:
getChactor函数具体流程图:
getRows()方法的流程图:
四、代码复审:
代码复审过程中,我查看了最初制定的代码规范,对比编程过程,命名采用驼峰命名法,不使用缩写形式,使用tab作为缩进,大小为4,在关键地方写上注释,这更便于理解;
在编写过程中,最初忽略了中文不算字符这个条件,就直接用ToString()函数获取字符串长度,没有加条件判断,导致最后返回的结果算上了中文,在代码复查过程中,我利用Ascii值0-127这一条件进行判断,排除了这个问题;
错误的:
FileStream fs = new FileStream(fileName, FileMode.Open);//打开文件 string str = Convert.ToString(fs.Length); fs.Close(); Console.Write("字符统计成功!\n"); return int.
错误结果:
正确的:
while ((str = fs.ReadLine()) != null) { for (i = 0; i < str.Length; i++) { if (str[i] >= 0 && str[i] <= 127) { // Console.Write(str[i]); count++; } } }
正确结果:
审查功能代码:
统计字符功能
public static string getChacactor(string filePath) { //统计字符数的方法 int i, count; count = 0; //打开文件 StreamReader fs = new StreamReader(filePath); string str = null; while ((str = fs.ReadLine()) != null) { for (i = 0; i < str.Length; i++) { if (str[i] >= 0 && str[i] <= 127) { // Console.Write(str[i]); count++; } } } fs.Close(); Console.Write("字符统计成功!\n"); Console.Write("characters:" + count + "\n"); return "characters:" + count + "\n"; }
统计行数功能:
public static string getRows(string filePath) { //统计行数的方法 FileStream fs = new FileStream(filePath, FileMode.Open);//打开文件 StreamReader sr = new StreamReader(fs, Encoding.Default);//用特定方式读取文件中信息 string s = sr.ReadToEnd();//读出所有信息 fs.Close(); sr.Close(); char[] c = { '\n' };//定义跳过的字符类型,换行符 string[] words = s.Split(c, StringSplitOptions.RemoveEmptyEntries);//将读出的信息按跳过的字符类型,分割成字符串 Console.Write("行数统计成功\n"); Console.Write("lines:" + words.Length + "\n"); return "lines:" + words.Length + "\n";//返回字符串的个数,即行数 }
统计单词功能:
public static string totalWord(string filePath) { //统计单词的总数 FileStream fs = new FileStream(filePath, FileMode.Open);//打开文件 StreamReader sr = new StreamReader(fs, Encoding.Default); string s = sr.ReadToEnd();//读取所有信息 Regex rg = new Regex("[A-Za-z-]+");//用正则表达式匹配单词 MatchCollection mc;//通过正则表达式所找到的成功匹配的集合 mc = rg.Matches(s); fs.Close(); sr.Close(); Console.Write("words:" + mc.Count + "\n"); return "words:" + mc.Count + "\n"; }
统计频率功能:
public static ArrayList countWord(string filePath) { //统计单个单词的出现次数 FileStream fs = new FileStream(filePath, FileMode.Open);//打开文件 StreamReader sr = new StreamReader(fs, Encoding.Default); string s = sr.ReadToEnd();//读取所有信息 Regex rg = new Regex("[A-Za-z-]+");//用正则表达式匹配单词 MatchCollection mc;//通过正则表达式所找到的成功匹配的集合 mc = rg.Matches(s); Hashtable wordList = new Hashtable(); for (int i = 0; i < mc.Count; i++) { string mcTmp = mc[i].Value.ToLower();//大小写不敏感 if (mcTmp.Length >= 4) { if (!wordList.ContainsKey(mcTmp))//第一次出现的单词添加为key { wordList.Add(mcTmp, 1); } else { int value = (int)wordList[mcTmp]; value++; wordList[mcTmp] = value; } } }
github桌面版记录提交历史:
五、单元测试展示:
测试指定文件路径:
cpu性能分析图:
cpu采样:
六、 异常处理说明:
提示`can't find file or file is empty!`
用户使用-r指令读取指定文件时,没有输入文件的路径地址。
提示`file path is invalid!`
用户使用了无法识别的指令。
提示`error:‘xxxx’ is an unknown command`
用户使用-o指令保存到指定路径下时,输入的路径无效。
提示`warning:‘zxlk’ is an invalid output path`
但因为执行此命令时,想要输出的结果已经得出,所以当指定路径错误时,仍然将结果保存到默认地址。
提示`save file to default path: xxxxx`
七、描述结对的过程:
- 刚开始结对的时候,我们先是明确了项目功能,主要有四大功能模块
- 两人进行分工,我完成的是字符统计和行数统计,搭档完成的是单词统计和top10的单词
- 明确各自任务后,我们制定了PSP表格,预计每个模块花费的时间,由于大家都缺乏项目经验,不能做到准确估计,所以有些偏差
- 接下来各自进行编码
- 完成后,上传github,合并代码
- 进行性能优化和单元测试
- 分析并处理异常
- 最后任务完成,编写博客
八、附加功能:
窗体应用:当用户导入文件的时候,没有输入Program里面的指令信息的时候,同样可以统计出字符串中的字符个数和单词个数,以及频率前10的单词,
具体代码如下:
Form1.Designer.cs:
namespace WindowsFormsApp2 { partial class Form1 { ////// 必需的设计器变量。 /// private System.ComponentModel.IContainer components = null; /// /// 清理所有正在使用的资源。 /// /// 如果应释放托管资源,为 true;否则为 false。 protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows 窗体设计器生成的代码 /// /// 设计器支持所需的方法 - 不要修改 /// 使用代码编辑器修改此方法的内容。 /// private void InitializeComponent() { this.button1 = new System.Windows.Forms.Button(); this.textBox1 = new System.Windows.Forms.TextBox(); this.button2 = new System.Windows.Forms.Button(); this.listBox1 = new System.Windows.Forms.ListBox(); this.label1 = new System.Windows.Forms.Label(); this.button3 = new System.Windows.Forms.Button(); this.button4 = new System.Windows.Forms.Button(); this.button5 = new System.Windows.Forms.Button(); this.button6 = new System.Windows.Forms.Button(); this.button7 = new System.Windows.Forms.Button(); this.textBox2 = new System.Windows.Forms.TextBox(); this.button8 = new System.Windows.Forms.Button(); this.SuspendLayout(); // // button1 // this.button1.Location = new System.Drawing.Point(537, 7); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(78, 30); this.button1.TabIndex = 0; this.button1.Text = "导入文件"; this.button1.UseVisualStyleBackColor = true; this.button1.Click += new System.EventHandler(this.button1_Click); // // textBox1 // this.textBox1.Location = new System.Drawing.Point(12, 12); this.textBox1.Name = "textBox1"; this.textBox1.Size = new System.Drawing.Size(496, 25); this.textBox1.TabIndex = 1; this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged); // // button2 // this.button2.Location = new System.Drawing.Point(642, 6); this.button2.Name = "button2"; this.button2.Size = new System.Drawing.Size(84, 31); this.button2.TabIndex = 2; this.button2.Text = "导入单词"; this.button2.UseVisualStyleBackColor = true; this.button2.Click += new System.EventHandler(this.button2_Click); // // listBox1 // this.listBox1.FormattingEnabled = true; this.listBox1.ItemHeight = 15; this.listBox1.Location = new System.Drawing.Point(12, 69); this.listBox1.Name = "listBox1"; this.listBox1.Size = new System.Drawing.Size(493, 259); this.listBox1.TabIndex = 3; // // label1 // this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(12, 40); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(82, 15); this.label1.TabIndex = 4; this.label1.Text = "文件未导入"; // // button3 // this.button3.Enabled = false; this.button3.Location = new System.Drawing.Point(543, 111); this.button3.Name = "button3"; this.button3.Size = new System.Drawing.Size(75, 47); this.button3.TabIndex = 5; this.button3.Text = "-c 查看字符数"; this.button3.UseVisualStyleBackColor = true; this.button3.Click += new System.EventHandler(this.button3_Click); // // button4 // this.button4.Enabled = false; this.button4.Location = new System.Drawing.Point(663, 112); this.button4.Name = "button4"; this.button4.Size = new System.Drawing.Size(75, 46); this.button4.TabIndex = 6; this.button4.Text = "-l 查看行数"; this.button4.UseVisualStyleBackColor = true; this.button4.Click += new System.EventHandler(this.button4_Click); // // button5 // this.button5.Enabled = false; this.button5.Location = new System.Drawing.Point(543, 184); this.button5.Name = "button5"; this.button5.Size = new System.Drawing.Size(75, 48); this.button5.TabIndex = 7; this.button5.Text = "-n 查看单词数"; this.button5.UseVisualStyleBackColor = true; this.button5.Click += new System.EventHandler(this.button5_Click); // // button6 // this.button6.Enabled = false; this.button6.Location = new System.Drawing.Point(663, 183); this.button6.Name = "button6"; this.button6.Size = new System.Drawing.Size(75, 49); this.button6.TabIndex = 8; this.button6.Text = "-w 查看高频单词"; this.button6.UseVisualStyleBackColor = true; this.button6.Click += new System.EventHandler(this.button6_Click); // // button7 // this.button7.Location = new System.Drawing.Point(593, 53); this.button7.Name = "button7"; this.button7.Size = new System.Drawing.Size(75, 29); this.button7.TabIndex = 9; this.button7.Text = "清空"; this.button7.UseVisualStyleBackColor = true; this.button7.Click += new System.EventHandler(this.button7_Click); // // textBox2 // this.textBox2.Location = new System.Drawing.Point(12, 370); this.textBox2.Name = "textBox2"; this.textBox2.Size = new System.Drawing.Size(496, 25); this.textBox2.TabIndex = 10; // // button8 // this.button8.Location = new System.Drawing.Point(593, 364); this.button8.Name = "button8"; this.button8.Size = new System.Drawing.Size(75, 33); this.button8.TabIndex = 11; this.button8.Text = "导出"; this.button8.UseVisualStyleBackColor = true; this.button8.Click += new System.EventHandler(this.button8_Click); // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(800, 450); this.Controls.Add(this.button8); this.Controls.Add(this.textBox2); this.Controls.Add(this.button7); this.Controls.Add(this.button6); this.Controls.Add(this.button5); this.Controls.Add(this.button4); this.Controls.Add(this.button3); this.Controls.Add(this.label1); this.Controls.Add(this.listBox1); this.Controls.Add(this.button2); this.Controls.Add(this.textBox1); this.Controls.Add(this.button1); this.Name = "Form1"; this.Text = "Form1"; this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Button button1; private System.Windows.Forms.TextBox textBox1; private System.Windows.Forms.Button button2; private System.Windows.Forms.ListBox listBox1; private System.Windows.Forms.Label label1; private System.Windows.Forms.Button button3; private System.Windows.Forms.Button button4; private System.Windows.Forms.Button button5; private System.Windows.Forms.Button button6; private System.Windows.Forms.Button button7; private System.Windows.Forms.TextBox textBox2; private System.Windows.Forms.Button button8; } }
Function.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Diagnostics; using System.IO; using System.Text.RegularExpressions; using System.Collections; namespace wordCount { //基类,基础的功能函数 public class Function { public static string getChacactor(string filePath) { //统计字符数的方法 int i, count; count = 0; //打开文件 StreamReader fs = new StreamReader(filePath); string str = null; while ((str = fs.ReadLine()) != null) { for (i = 0; i < str.Length; i++) { if (str[i] >= 0 && str[i] <= 127) { // Console.Write(str[i]); count++; } } } fs.Close(); Console.Write("字符统计成功!\n"); Console.Write("characters:" + count + "\n"); return "characters:" + count + "\n"; } public static string getRows(string filePath) { //统计行数的方法 FileStream fs = new FileStream(filePath, FileMode.Open);//打开文件 StreamReader sr = new StreamReader(fs, Encoding.Default);//用特定方式读取文件中信息 string s = sr.ReadToEnd();//读出所有信息 fs.Close(); sr.Close(); char[] c = { '\n' };//定义跳过的字符类型,换行符 string[] words = s.Split(c, StringSplitOptions.RemoveEmptyEntries);//将读出的信息按跳过的字符类型,分割成字符串 Console.Write("行数统计成功\n"); Console.Write("lines:" + words.Length + "\n"); return "lines:" + words.Length + "\n";//返回字符串的个数,即行数 } public static string totalWord(string filePath) { //统计单词的总数 FileStream fs = new FileStream(filePath, FileMode.Open);//打开文件 StreamReader sr = new StreamReader(fs, Encoding.Default); string s = sr.ReadToEnd();//读取所有信息 Regex rg = new Regex("[A-Za-z-]+");//用正则表达式匹配单词 MatchCollection mc;//通过正则表达式所找到的成功匹配的集合 mc = rg.Matches(s); fs.Close(); sr.Close(); Console.Write("words:" + mc.Count + "\n"); return "words:" + mc.Count + "\n"; } public static ArrayList countWord(string filePath) { //统计单个单词的出现次数 FileStream fs = new FileStream(filePath, FileMode.Open);//打开文件 StreamReader sr = new StreamReader(fs, Encoding.Default); string s = sr.ReadToEnd();//读取所有信息 Regex rg = new Regex("[A-Za-z-]+");//用正则表达式匹配单词 MatchCollection mc;//通过正则表达式所找到的成功匹配的集合 mc = rg.Matches(s); Hashtable wordList = new Hashtable(); for (int i = 0; i < mc.Count; i++) { string mcTmp = mc[i].Value.ToLower();//大小写不敏感 if (mcTmp.Length >= 4) { if (!wordList.ContainsKey(mcTmp))//第一次出现的单词添加为key { wordList.Add(mcTmp, 1); } else { int value = (int)wordList[mcTmp]; value++; wordList[mcTmp] = value; } } } fs.Close(); sr.Close(); string[] keys = new string[wordList.Count]; int[] values = new int[wordList.Count]; ArrayList retureList = new ArrayList(); wordList.Keys.CopyTo(keys, 0); wordList.Values.CopyTo(values, 0); Array.Sort(values, keys); Array.Reverse(keys); try { for (int j = 0; j < 10 && j < wordList.Count; j++) { Console.WriteLine(keys[j] + ":" + wordList[keys[j]]); retureList.Add(keys[j] + ":" + wordList[keys[j]]); } } catch(IndexOutOfRangeException e) { Console.WriteLine("Exception caught:{0}", e); } return retureList; } } //派生类,优化用户体验 class FunctionEX { } }
Program.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApp2 { static class Program { ////// 应用程序的主入口点。 /// [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } }
界面展示:
九、总结:
非常感谢我的搭档,他做了很多代码优化,也是他完成的附加功能。通过这次个人项目,我也收获了很多,比如程序的设计实现需要先设计再去实现,设计和编译一样重要,完成任何一个程序,需要实现设计方案,避免后面的错误和程序不完善等问题。同时,代码的测试也占有相当的比例,通过各方面的测试避免程序出现错误和对用户体验度的完善。最后,完成一个完整的程序的时候,一定的代码功底就显得尤为重要,这是需要不停积累与练习的。