zoukankan      html  css  js  c++  java
  • 福大软工1816 · 第二次作业

    1.在文章开头给出Github项目地址。

    git项目地址:https://github.com/Stellaaa18/personal-project

    2.给出PSP表格。

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

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

    程序要求读入一个文本文件,统计该文件的字符数,单词数,有效行数以及输出词频最高的10个单词。我是用正则表达式做的,按行从文件读取内容,每当匹配到一个符合条件的单词时,就将其存进map里面。然后遍历map,将单词及其出现次数放到vector里面,排序输出词频最高的10个单词。

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

    关于代码结构

    第一次写的时候,我是分成了以下几个函数,写在同一个源文件里面。

    • CountChar() : 用于统计文件的字符个数
    • CountLines() : 统计文件的有效行数
    • CountWords() : 统计有效单词个数
    • WordsFrequency() : 统计各个单词出现的次数,并将词频最高的10个单词输出到result.txt文件中

    后面看到接口封装的要求,参考了赵畅同学的代码组织结构。

    工程框架图如下:

    设计的单元测试如下:

    单元测试名称 测试内容 测试函数 测试结果
    AllEmptyChar 统计空白字符(空格,换行符,制表符) CountChar() 通过
    OtherASCII 统计非数字字母的ascii码符号 CountChar() 通过
    IncludeEmptyLines 统计含有空行的文件的行数 CountLines() 通过
    NotValidWords 文本都是无效单词(例如abc,123file) CountWords() 通过
    WordSperator 分隔符是其他ascii字符(例如"?","!","$") CountWords() 修改后通过
    EmptyFile 测试空文件 CountChar(),CountLines(),CountWords() 通过
    CaseInsentitive 测试是否不区分大小写(file和FILE为同一个单词) WordsFreq() 通过
    CheckOrder 词频相同时是否按字典序 WordsFreq(),TopTenWords() 通过

    以下部分单元测试代码:

    namespace EmptyFile//测试空文件
    {
    	TEST_CLASS(UnitTest1)
    	{
    	public:
    
    		TEST_METHOD(TestMethod1)
    		{
    			char filename[105] = "EmptyFile.txt";
    			int characters = CountChar(filename);
    			int lines = CountLines(filename);
    			int words = CountWords(filename);
    			Assert::IsTrue(characters == 0 && lines == 0 && words == 0);
    		}
    	};
    }
    
    extern map<string, int> M;
    namespace CaseInsentitive //测试是否不区分大小写
    {
    	TEST_CLASS(UnitTest1)
    	{
    	public:
    
    		TEST_METHOD(TestMethod1)
    		{
    			char filename[105] = "CaseInsentitive.txt";
    			init();
    			WordsFreq(filename);
    			map<string, int>::iterator it = M.begin();
    			Assert::AreEqual(it->second, 3);
    		}
    
    	};
    }
    

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

    将main()函数循环执行10000次,得到的结果如下:

    程序一共运行了44.21秒,消耗最大的是CountWor()函数。从这张图可以看到,正则表达式有关的函数regex_serch和迭代器matcher消耗的时间也很大,紧跟在main()函数之后。
    从下图可以看出,CountWord()函数里面有很多和正则表达式相关的操作,故而消耗的时间大。

    代码覆盖率截图


    从图中可以看出,除了分析命令行参数的函数AnalyseParameter(int argc, char **argv)代码覆盖率较低外,其他的覆盖率都很高。因为AnalyseParameter(int argc, char **argv)主要用来处理异常情况,而测试时用的样例大部分传参是正确的,故该函数覆盖率较低。

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

    在计算单词频率时,用到了正则表达式,参考了这篇文章C++语言,统计一篇英文文章中的单词数(用正则表达式实现)
    具体代码如下:

    	string str;
    	regex rx("\b[a-zA-Z]{4}[a-zA-Z0-9]*");//至少以四个英文字母开头
    	while (getline(file, str)) {
    		smatch m;
    		while (regex_search(str, m, rx)) {
    			string s = m[0];
    			for (int i = 0; s[i] != ''; i++) {
    				if (s[i] >= 'A'&&s[i] <= 'Z') s[i] += 32;//大写转小写
    			}
    			M[s]++;//将转换成小写的有效单词存进map里
    			str = m.suffix().str();
    		}
    	}
    

    然后将map里的内容复制到vector里面,直接排序输出前10个词。

    for (auto x : M) {
    		ans.push_back(x);
    	}
    	sort(ans.begin(), ans.end(), cmp);//排序
    	int count = 10;
    	//输出词频最高的10个单词
    	ofstream fout("result.txt");
    	for (auto x : ans) {
    		if (!count) break;
    		fout << "<" << x.first << ">: " << " " << x.second << endl;//输出到文件
    		count--;
    	}
    	fout.close();
    

    异常处理

    int AnalyseParameter(int argc, char **argv)
    {
    	if (argc != 2) { //传入参数有误时,会给出提示
    		cout << "argument error.
    ";
    		return -1;
    	}
    	ifstream fin(argv[1]);
    	if (!fin) { //若该文件不存在,也会报错
    		cout << "can't open the file.
    ";
    		return -1;
    	}
    	
    	return 1;
    }
    

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

    一开始拿到题目时,其实是有点慌的,不知道要从哪里开始做。后面冷静下来,一步步分析,先把主要核心功能细分成统计字符数,行数,单词数及计算词频四个部分,一个个写出来。后面再写个函数解析命令行参数,至此基本功能都已经完成了,然后就剩下单元测试和性能改进了。
    这次项目最大的收获就是,遇到难题不应该害怕,分成一个个更小的任务,逐个去解决,好像也没想象中那么困难。

    8.Refrence

    用正则表达式统计单词数: https://www.bbsmax.com/A/A2dmM8qgde/
    赵畅同学博客: http://www.cnblogs.com/ZCplayground/p/9607027.html

  • 相关阅读:
    modf()函数
    面向对象编程五大原则
    .Net网络资源
    整理CentOS常用命令
    在RHEL5上安装oracle10gLinux
    strchr()函数
    swab函数
    Strstr()函数
    tmpnam函数
    strdup ()函数
  • 原文地址:https://www.cnblogs.com/Stella12/p/9622734.html
Copyright © 2011-2022 走看看