• 福大软工1816 · 第二次作业


    福大软工1816 · 第二次作业 - 个人项目

    一、Github项目地址

    Github项目地址

    二、PSP表格

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

    三、解题思路描述

    解题思路描述。即刚开始拿到题目后,如何思考,如何找资料的过程。

    1、首先,题目要求在程序中对指定文件进行读写操作,并且输入文件名以命令行参数传入。之前有稍微接触过文件读写和命令行传参的相关内容,因此对这部分的要求并未太过纠结。
    2、题目的具体要求是统计文件的字符数、有效行数、合法单词数,并字典序输出词频最高的10个合法单词。

    • 统计文件的字符数。可以从文件中依次读取字符,通过判断读取到的字符是否为ASCII码进行字符统计。
    • 统计文件的有效行数。题目要求“任何包含非空白字符的行,都需要统计。”我的理解是如果一行中只包含空格或制表符,则将其视为空白行,不予统计。可以用getline()函数从文件中逐行读取,去除行中的空格和制表符,如果处理后的字符串的长度仍大于0,则将其视为有效行。思路参考博客1
    • 统计文件中的合法单词数,并字典序输出词频最高的10个合法单词。考虑这个功能的实现花费了我最多的时间和精力。题目对单词的形式提出了要求,即“单词:至少以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写。”另外,题目中提到了单词的分隔符是非字母数字符号,因此就不能简单的根据空格提取单词,提取单词的过程中还要进行对单词格式的检查。所以,我用逻辑判断截取合法单词,用map进行词频统计,最后借助vector输出词频最高的10个合法单词。思路和实现参考博客2

    四、设计实现过程

    设计实现过程。设计包括代码如何组织,比如会有几个类,几个函数,他们之间关系如何,关键函数是否需要画出流程图?单元测试是怎么设计的?

    1、代码组织

    我把程序的主要功能:统计字符数、统计有效行、统计合法单词数和按字典序输出词频最高的10个合法单词封装成了一个Statistics类,没有将文件的操作单独封装成类,在主函数中进行文件的打开和关闭,将文件参数传给类中的统计功能函数,实现统计功能。关系如下:

    2、关键函数流程图

    int words(ifstream& in)统计单词数及词频流程图:

    补充:在i=str.length()-1时,情况1:str[i]为字母,此时若flag>=3,截取合法单词;情况2:str[i]为数字,此时若flag>=4,截取合法单词;情况三:str[i]为非数字字母字符,无特殊处理。
    补充2:在进行截取前,如果所选定截取部分的前一个字符为数字,判断为不合法单词,放弃截取。

    3、单元测试

    我在这一步折腾了很久,先是找资料学习如何进行单元测试,之后在实践中也遇到了很多的问题,特别是没有把测试用的文档放在正确的地方导致测试结果老是失败。
    为了更方便地进行单元测试,我对原先程序的封装进行了改动,实施了单元测试之后切实感受到了它的好处,利用单元测试可以很好的检测程序中的功能,我在进行单元测试的过程中就找到了我程序中的几个bug,如果将来要进行大的项目的建设,单元测试带来的方便应该会更加显著。
    我设计的单元测试有:回车字符的统计、字符数的统计、有效行数的统计、单词数的统计、合法单词的提取、是否转换为小写字母、词频统计是否正确、单词是否按字典序排列、空文档的统计、仅含空格、制表符、回车的文档的统计。
    单元测试部分代码:

    namespace UnitTest4//测试单词数的计算
    {
    	TEST_CLASS(UnitTest1)
    	{
    	public:
    
    		TEST_METHOD(TestMethod1)
    		{
    			ifstream in;
    			in.open("test4.txt", ios::in);
    			Statistics s;
    			Assert::IsTrue(s.words(in) == 3);
    		}
    	};
    }
    

    单元测试结果如下:

    单元测试得到的测试覆盖率截图:

    主函数里未被覆盖部分为错误处理代码。

    五、性能分析和改进

    记录在改进程序性能上所花费的时间,描述你改进的思路,并展示一张性能分析图(由VS2017的性能分析工具自动生成),并展示你程序中消耗最大的函数。

    选择33.6M的txt文档进行性能分析。

    性能分析图:

    消耗最大的函数:

    消耗最大的函数是Statistics类里统计单词数及词频的函数words(),其中,消耗主要在对map的操作以及文件的读取上。

    六、代码说明

    展示出项目关键代码,并解释思路与注释说明。

    1、统计行数

    去除一行中的空格和制表符,如果该行长度仍大于0,说明其为有效行数。
    主要代码:

    while (getline(in, str))
    	{
    		str.erase(std::remove(str.begin(), str.end(), ' '), str.end());//删除一行中的空格
    		str.erase(std::remove(str.begin(), str.end(), '	'), str.end());//删除一行中的制表符
    		if (str.length() > 0) //如果删除制表符和空格之后的一行数据还有其他字符就算有效行
    		{
    
    			line++;
    		}
    	}
    

    2、按字典序输出词频最高的10个单词

    借助vector输出:将储存于map中的各单词的词频导入vector,接着对vector从大到小排序,最后将vector中排在前十的数字依次与map中记录的词频比对,输出最先匹配到的单词和词频,置输出的单词的词频为0。
    主要代码:

    	map<string, int>::iterator it;
    	for (it = word.begin(); it != word.end(); it++)
    	{
    		int t = (*it).second;
    		a.push_back(t);
    	}
    	sort(a.begin(), a.end(), cmp);
    	if (wnum == 0)
    	{
    		out << "该文档不存在合法单词!" << endl;
    	}
    	else
    	{
    		for (int i = 0; i < wnum && i < 10; i++)
    		{
    
    			int t = a[i];
    			for (it = word.begin(); it != word.end(); it++)
    			{
    				if ((*it).second == t)
    				{
    					out << "<" << (*it).first << "> :" << t << endl;
    					(*it).second = 0;
    					break;
    				}
    			}
    		}
    	}
    

    3、错误处理部分

    处理命令行输入参数错误

    if (argc != 2)//检测输入的命令行参数是否正确
    {
    	cout << "输入参数错误!" << endl;
    	exit(1);
    }
    

    处理输入输出文件打开失败

    infile.open(argv[1], ios::in);
    	if (infile.fail())
    	{
    		cout << "输入文件打开失败!" << endl;
    		exit(1);
    	}
    	outfile.open("result.txt", ios::out);
    	if (outfile.fail())
    	{
    		cout << "输出文件打开失败!" << endl;
    		exit(1);
    	}
    

    七、心路历程与收获

    结合在构建之法中学习到的相关内容与个人项目的实践经历,撰写解决项目的心路历程与收获。

    这次的作业花费了我很多的时间和精力,我的能力比我想象中的还要不足,作业中列出了一项项具体的要求,所以我在实践中就按照这一项项要求去思考、搜索、学习,在搜索过程中我找到了很多比我原先想的要方便得多的解决方法,见识了很多新知识和新技术,可以说,我在这次个人项目里做的最多的就是学习,学习新知识,比如软工的流程和规范、字符串处理、STL的应用,学习新技术,比如vs的单元测试、性能分析等等,这些新知识和新技术算是我这次最大的收获了吧。另外,写博客一直是很让我头疼的一件事,我在写博客上也花费了相当多的时间和精力,怎么在博客中清楚地表达自己的想法和经历,展现自己的成果和收获是一个很重要的能力,我在这方面有很大的欠缺,有待加强,希望以后写博客能成为一项我喜欢的事。

    参考资料:
    博客1:https://blog.csdn.net/haomingzidoumeilea/article/details/8977012
    博客2:https://blog.csdn.net/hisfox/article/details/80013414#t3

  • 相关阅读:
    ListView添加HeaderView的顺序问题
    shape与selector配合使用实现Tab下划线效果
    在java代码中设置TextView的字体大小,单位设为sp
    一个textView中的文字设置成两种颜色
    Android studio生成类图
    linux常用命令总结
    主机CPU与显卡选择
    电脑与显示器四种接口
    Linux shell
    Linux 常用命令二
  • 原文地址:https://www.cnblogs.com/z031602148/p/9629721.html
Copyright © 2020-2023  润新知