天天看点

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

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

一、Github项目地址

Github项目地址

二、PSP表格

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

三、解题思路描述

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

1、首先,题目要求在程序中对指定文件进行读写操作,并且输入文件名以命令行参数传入。之前有稍微接触过文件读写和命令行传参的相关内容,因此对这部分的要求并未太过纠结。

2、题目的具体要求是统计文件的字符数、有效行数、合法单词数,并字典序输出词频最高的10个合法单词。

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

四、设计实现过程

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

1、代码组织

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

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

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);
		}
	};
}
           

单元测试结果如下:

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

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

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

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

五、性能分析和改进

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

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

性能分析图:

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

消耗最大的函数:

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

消耗最大的函数是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(), '\t'), 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