《构建之法》第四次作业——结队编程

 一、合作 :

github项目地址https://github.com/Shen-MoMo/WordCount 

合作同学作业地址:https://www.cnblogs.com/gbx123/p/11640432.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、我的思路:

搭档最先完成了部分基础功能的实现以及代码的架构,但由于一开始领航出现失误,遗留下了一些问题,随后加以改正。

  • 在编写totalWord()函数时,用StreamReader方式读取出文件所有信息,然后利用正则表达式匹配单词,取出所有单词得到集合mc(MatchColletion)。
  • 在编写countWord()函数时,用同上的方法得出集合mc,首先让集合mc中的元素对大小写不敏感,然后将第一次出现的单词作为key传入哈希表,value为1,当出现重复的单词时value加1。随后对哈希表进行排序,得到出现频率最大的10个单词
  • 在编写countWord()函数之前,每个函数都只返回一个int型的数据就足够,但countWord()函数需要返回很多数据,所以代码需要重构。
  • 将函数的返回值改为了字符串String,返回值存入动态数组returnTxt。
  • 编写-r指令,添加指定读取文件地址的功能(默认读取DEBUG目录下的file.txt文件)。
  • 编写save()函数,添加指定保存地址和指定保存文件的功能。将动态数组returnTxt中的的元素输出到文件中(默认保存在DEBUG目录下)。
  • 最后检查异常处理,进行测试。

3、流程图:

 

 

四、代码复审:

代码复审过程中,我查看了最初制定的代码规范,对比编程过程,命名采用驼峰命名法,不使用缩写形式,使用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";
        }
View Code

统计行数功能:

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";//返回字符串的个数,即行数          
        }
View Code

统计单词功能:

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";
        }
View Code

统计频率功能:

 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;
                    }
                }
            }
View Code

五、单元测试展示:

 测试指定文件路径:

cpu性能分析图:

cpu采样:

 

 

 

 

六、 异常处理说明:

用户使用-r指令读取指定文件时,输入的文件路径地址无效,文件是空文件,或指定的是目录而非文件。
提示`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
    {
        /// <summary>
        /// 必需的设计器变量。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 清理所有正在使用的资源。
        /// </summary>
        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows 窗体设计器生成的代码

        /// <summary>
        /// 设计器支持所需的方法 - 不要修改
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        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;
    }
}
View Code

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
    {

    }

}
View Code

Program.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp2
{
    static class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}
View Code

界面展示:

九、总结:

第一次进行结对编程,有很多没能做好的地方。一旦最初的领航出现偏差,后面的航行就会离最初的目的地偏差越大。在代码的设计是也出现了,很多不合理的地方。比如每次调用函数,都要重新打开一次文件读取内容,如果文件内容较大那么速度会变慢。这一次的作业体验到了结对编程的重要性,但由于经验不足,仍然没有做好领航的工作,导致后期代码反复修改。例如上面展示的代码使用的变量命名为fileName,到了后来才发现实际上储存的是文件路径,因此改名为filePath更加准确,等等类似的问题出现。

 结对编程过程中,搭档能提醒自己的代码出现了错误,反馈错误信息,编写的过程中能及时修改错误。若能做好领航的工作,那么工作的效率会非常高。

附录:Github管理记录

 

posted @ 2019-10-14 14:46  傻墨  阅读(161)  评论(1编辑  收藏  举报