• #软工实践-个人项目-词频统计


    Github项目地址

    https://github.com/pandaeathzr/personal-project


    PSP表格:

    PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟
    Planning 计划
    · Estimate · 估计这个任务需要多少时间 10 0
    Development 开发
    · Analysis · 需求分析 (包括学习新技术) 120 240
    · Design Spec · 生成设计文档 20 30
    · Design Review · 设计复审 20 10
    · Coding Standard · 代码规范 (为目前的开发制定合适的规范) 60 10
    · Design · 具体设计 60 60
    · Coding · 具体编码 360 540
    · Code Review · 代码复审 60 120
    · Test · 测试(自我测试,修改代码,提交修改) 60 120
    Reporting 报告
    · Test Repor · 测试报告 60 60
    · Size Measurement · 计算工作量 20 20
    · Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 60 120
    合计 910 1330

    解题思路:

    实现基本功能:

    • 统计文件的字符数:难度系数较低,在不考虑汉字的情况下,只需要记录读入字符的数量即可。
      -** 统计文件的有效行数:**初步设想有效行只需判断该行有非空字符,且以换行符结尾或者文件结尾。

    • 统计文件的单词总数:

      • 要求单词至少以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写。只需要连续确定4个英文字符后,再遇上分割符确定一个单词,然后记录即可。
      • 分割符的定义是非字母数字符号,例如!@#¥%……&*还有空格、转义字符、换行符。
    • 统计文件中各单词的出现次数:把每个单词记录下来,作为一个key放进map容器中,出现相同单词的时候对应的值增加1。并且由于单词不区分大小写,在记录单词前必须将其转换成小写字母(输出的单词统一为小写格式)。由于采用map容器,输出即可按照字典序。

    其他要求:

    • 使用Github来管理源代码和测试用例
    • 提交的代码要求经过Code Quality Analysis工具的分析并消除所有的警告。
    • 使用性能分析工具Studio Profiling Tools来找出代码中的性能瓶颈并进行改进。
    • 使用单元测试对项目进行测试,并使用插件查看测试分支覆盖率等指标

    惭愧的说以上的要求都是之前所没有涉及到的,所以对于这些工具,可能需要花费更多的时间去学习或者调试。


    具体实现:

    • 程序的结构大致如下:

    • 统计字符数以及有效行数相对比较简单。故在此不对其展开具体实现。

    • 对于统计单词数目,根据我们之前的想法,构造了一台字母的有穷自动机。在连续遇到4个字母前,若遇到非字母字符,则返回初始的状态(由于作图工具的限制,初始状态少了个>图形)。遇到连续的4个字母后,遇到字母和数字都会在当前状态下循环,除非遇到了我们定义的分隔符号,则终止并记录单词,存入map中。

    • 部分代码:
    	···
    	map<string, int> strMap;
    	while (!infile.eof())
    	{
    		
    		infile >> c;  											  //读字符
    
    		if ((c >= 'a'&&c <= 'z') || (c >= 'A'&&c <= 'Z'))
    		{
    			c = lower(c);
    			char word[20] = {};
    			int k = 0, c_count = 0;
    			int num = 0;
    			int flag = 0;
    			while ((c >= 'a'&&c <= 'z') || (c >= 'A'&&c <= 'Z'))  //判断是否为四个连续字母
    			{
    				word[k++] = c;
    				infile >> c;
    				c = lower(c);
    				num++;
    				if (num > 3)
    				{
    					flag = 1;
    					num = 0;
    					break;
    				}
    			}
    			while (whether_char(c)&&!infile.eof())           
    				 //whether_char()判断是否为分割符
    			{												  
    				//若不为分割符号且不是文件结尾读取不断后面字符	
    				if (flag == 1)								  
    				{
    					c = lower(c);
    					word[k++] = c;
    				}
    				infile >> c;
    			}
    			if (flag == 1)                                  
    			{
    				if (strMap.find(word) != strMap.end())	strMap[word]++; 
    			// 统计单词,若存在相同单词,则词频+1
    				else strMap[word] = 1;
    			}
    
    		}
    	}
     	···
    

    性能测试:

    • 性能测试的结果如下所示

    • 显然自己代码的瓶颈是在于无脑采用了map容器。这样虽然可以比较方便,或者说比较偷懒,但是的的确确给自己的程序性能带来了很大的影响。

    • map是平衡二叉树,适用于快速检索。由于二叉平衡树插入机制以及内存分配的原因,它不适合频繁的内存操作,包括声明map对象、插入数据、删除数据。正确的用法是首先把所有的数据加工处理准备好,然后交给map存储,以便检索。[1]

    • 解决的方法:可以采用hashmap增大命中率。这样可能会让程序的性能有所改善。但是由于时间的原因,还没有采取相应的措施去改善代码。


    单元测试:

    单元测试的结果如下所示:

    • 所采用的10个测试均能较好的完成。

    • |单元测试名称|内容介绍|
      |:--|:--|
      |EmptywordText|空文件测试|
      |Up_low_wordEuqalText|大小写相等测试|
      |Empty_zero_lineText|0行测试|
      |IsNotWordText|错误单词测试|
      |The_FistWordText|单词排序输出词频数最大的测试|
      |ViodWordText|不存在单词测试测试|
      |ViodlinesText|不存在有效行测试|
      |ErrorFilenameText|错误文件名测试|
      |VerylargewordText|超大单词测试|
      |SeparatorText|分隔符测试|

    • 截取了部分的单元测试代码如下:

      • 空文件测试
      • 大小写相等测试
      • 非单词测试
    namespace UnitTest1
    {		
    	TEST_CLASS(EmptywordText)
    	{
    	public:
    		
    		TEST_METHOD(TestMethod1)
    		{
    			// TODO: 在此输入测试代码
    			int count = CountChar("Emptyword.txt"); // 传入一个空的txt的文件
    			Assert::IsTrue(count == 0);				// 它的值应该为0,测试通过
    		}
    
    	};
    	TEST_CLASS(Up_low_wordEuqalText)
    	{
    	public:
    
    		TEST_METHOD(TestMethod1)
    		{
    			// TODO: 在此输入测试代码
    			int count_up = CountChar("upperword.txt"); 
    			int count_low = CountChar("lowerword.txt"); //传入两个文件,一个为大写LIFE,一个为小写life
    			Assert::IsTrue(count_up == count_low);  // 两个返回值应该相等,测试通过
    		}
    	};
    		TEST_CLASS(IsNotWordText)
    	{
    	public:
    
    		TEST_METHOD(TestMethod1)
    		{
    			// TODO: 在此输入测试代码
    			int count = Countwords_num(Countwords("NoWord.txt")); //传入一个非单词的文件,例如123file
    			Assert::IsTrue(count == 0);						 // 因为不是单词,所以返回值应该为0,测试通过
    		}
    
    	};
    }
    
    

    代码覆盖率:

    代码覆盖率的结果如下图所示:


    异常处理:

    如果输入了一个不存在的文件名,则提示文件名错误

    int CountChar(char *filename)
    {	
    	···
    	else infile.open(filename);
    
    	if (!infile)
    	{ 
    		cout << "you filename is error" << endl;
    		return 0;
    	}
    	···
    
    

    总结与感悟

    每天起床第一句,先给自己打个气!

    我对软工实践的态度还是挺积极的。但是由于开学季、新生周,自己还担任班导以及一些职务,处理这些非学习上的事情可以说占据了我这一周大部分的时间。每天都要并发执行好多好多的事情,所以在作业的前半段时间,只能在深夜里留出一两个小时做软工作业。当时的念头居然是为了做作业舍不得睡觉。
    ┗|`O′|┛ 嗷~~

    态度摆是摆在这里了,然而这次作业还是十分受限于自己的硬核实力。作业上所提出的其他要求,在之前的日子里基本上是没有碰到过的。

    • 要求使用VS2017的时候我还觉得换了IDE挺麻烦的(现在使用起来似乎蛮不错)。

    • Github:之前只是皮毛的建了个基于hexo的个人主页。并没有很好的理解Github的作用。直到在写完基本功能后,尝试去学习了下Github的入门教程,仿佛发现了一片新天地。每次修改完一次的功能,就commit一下。然后可以看到自己的成长的过程,这种感觉很棒。但是自己目前仍然对github的一些命令不大熟悉,导致自己曾经有一次把库搞炸了,重头foke了一下。同时,在使用的过程中,阅读了畅畅酱博客以及构建之法的相关知识。知道了注释以及这些信息的提交尽量不要使用中文,所以在这个过程中逐渐养成了这个习惯。自己的英语功底不好,有时候还要借助谷歌翻译,但是这也是一个进步。而缺点在于,还是没有能够清晰的给出commit信息。这个需要慢慢培养一下自己。

    • 性能测试:性能测试前,知道自己写的代码有点渣,也清楚在那些部分肯定是泛红的一片。导致这中原因,主要在于自己没有能够好好的分析需求,追求时间,仓促编码,而省下来的这部分时间却在编码的过程中翻倍付出。软工实践不是那种走一步看一步的事情。如果没有能够以一个相对大局的观念,统筹规划一下,组织好代码结构,写好对应的流程图,会对之后的编码以及代码的性能,可扩展性有很大的帮助的。

    • 单元测试:这是个很神奇的东西。这是我稍微弄懂单元测试后的想法。而单元测试时,我在这卡壳了很久,一些链接文件刚开始也没搞好。然后陷入了一个绝望的,疯狂修改设置的循环中,导致了原本还能生成运行的exe文件都消失了。但是看着别人的博客的介绍,感觉这个应该不是一个卡住不动的地方。OK,既然陷入了一个混乱中,不如直接一刀割开。把自己的本地仓库删了,重新clone了一次(这里就体现了Github的好处)。谨慎细致的配置单元测试,发现其实使用它还是蛮容易的。看着自己马马虎虎的10个单元测试还是有点开心的。

    • 代码覆盖率:这个不是很懂就是了。时间关系就不多细细讲了,没有遇到什么卡住的地方。看到自己的测试代码覆盖率也仅仅平均80%,说明了前面的测试还有很多不足的地方需要细细去调整。

    这里感谢一下畅畅酱,在它的博客下,我能够比较快速的找到了自己工具需求、以及对我写博客和单元测试有很大的指导。然后在昨天也很幸运的成立了团队项目的初创四人帮,有了一个大概的方向。软工实践的第二次作业就让我知道的前面的路途不易,所以自己的实力还需要努力提高,才能更好的匹配上自己的队友,与大家并肩作战~!

    同时感谢助教提供的excel表格转md文件~


    后记:

    分享五月天

  • 相关阅读:
    PIC18F2455/2550/4455/4550之通用串行总线USB
    今天,一个新的起点
    WM_COPYDATA消息
    图片浏览(附带样式+效果)
    这条路,走远一点,再远一点
    html之table(10种表格)
    数据导出成Excel
    .net 附件下载
    .net Repeater嵌套的数据绑定问题
    AjaxPro.2.dll的使用方法,以实例讲解。
  • 原文地址:https://www.cnblogs.com/Huzr/p/9636808.html
Copyright © 2020-2023  润新知