软工实践第二次作业2.0
github地址
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
Estimate | 估计这个任务需要多少时间 | 1220 | |
Development | 开发 | ||
Analysis | 需求分析 (包括学习新技术) | 120 | 180 |
Design Spec | 生成设计文档 | 20 | 20 |
Design Review | 设计复审 | 10 | 10 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
Design | 具体设计 | 30 | 30 |
Coding | 具体编码 | 500 | 500 |
Code Review | 代码复审 | 320 | 360 |
Test | 测试(自我测试,修改代码,提交修改) | 60 | 60 |
Reporting | 报告 | ||
Test Repor | 测试报告 | 60 | 60 |
Size Measurement | 计算工作量 | 60 | 50 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 90 | 60 |
合计 | 1220 | 1340 |
计算模块接口的设计与实现过程。
-
定义了四个函数体头文件。
-
各个接口间独立,分别向文件中输入结果。
-
算法的关键在于建立map,key是单词,value是出现次数。
-
在统计频率时,原本想的是记下来每个单词出现的频率后再排序,这样会用到O(N*log N)。在查阅一些资料后,后来才发现可以直接保存频率最高的几个单词,遍历的同时替换就可以了,降到了O(N)。下图为排序过程
###性能改进
(1)
输入改进,具体见1.0版。
(2)
- 因为算法是统计单词用的是链表,所以插入单词时要遍历整个链表,随着单词数的增加,要遍历的链表越来越长。当运行200万字数的文档是,结果一直跑不出来。
- 于是我想到了第一种改进策略,链表一开始插入的时候不找相同的单词,建立一个首字母链表,记录每个首字母单词的位置,插入到首字母相同的单词后,如果该单词的首字母没有在首字母链表中,就将该单词插入在首字母链表中。这样首字母链表最多有26个节点。如果一个单词要插入到链表中最多遍历26遍。这样就是把所有单词加入到了链表中,之后就是将相同的单词合并,这样一个单词就不用遍历整个链表找自己的相同的单词,只需要在与自己首字母相同的范围寻找就行了。这样运行200万字的文档就能跑出来了,但是要45秒。(具体代码可看github注释部分)
- 尽管相同单词合并时时间减小,但是仍要遍历与自己首字母相同的单词链表,耗时o(n!),显然耗时太久了。在与同学的交流中,知道了map这种方便的存在,他用红黑二叉树,插入时间会大大变短。于是我去学了下map的使用。就把链表改成了map,运行时间果然变短了,运行200万字的文档要20s左右。
单元测试
单元测试结果
展示测试求字符总数的函数的单元测试代码
#include "stdafx.h"
#include "CppUnitTest.h"
#include"../wordCount/readchars.h"
#include"../wordCount/readlines.h"
#include"../wordCount/readword.h"
#include"../wordCount/sort.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTest2
{
TEST_CLASS(UnitTest2)
{
public:
TEST_METHOD(TestChars)//测试字符数函数
{
// TODO: 在此输入测试代码
string filename = "C:/Users/Mac/Desktop/1234.txt";
int file_chars = 0;
Assert::AreEqual(0, readchars(filename, file_chars));
}
}
思路:如果可以正确打开文件并且成功运行会返回0,测试该模块是否与0相等,附上读取字符数的代码。
#include"pch.h"
#include"readchars.h"
#include"structword.h"
#include<fstream>
using namespace std;
/**********************读取字符*********************/
int readchars(string filename, int file_chars)
{
ifstream file(filename);
//FILE *file = _fsopen(filename, "r", _SH_DENYNO);
if (!file)
{
printf( "打开文件错误!" );
return 0;
}
char chars = -1;
while (1)
{
//chars = fgetc(file);
chars = file.get();
if (chars == EOF)
break;
file_chars++;
}
std::ofstream openfile("result.txt", std::ios::trunc);
openfile << "characters: " << file_chars << endl;
openfile.close();
return 0;
}
单元测试测试覆盖率
异常处理模块
异常处理:
路径无效时显示无法打开文件并退出
ifstream file(filename);
if (!file)
{
printf( "打开文件错误!" );
return 0;
}
单元测试
TEST_CLASS(UnitTest2)
{
public:
TEST_METHOD(TestChars)
{
// TODO: 在此输入测试代码
string filename = "mm";
int file_chars = 0;
Assert::AreEqual(0, readchars( filename, file_chars));
}
filename为无效地址
结果通过测试
总结
我用了很长的时间来做这次作业,在这次作业的过程中遇到了很多困难也学到了很多知识。通过这次学习我知道了以后可以通过单元测试来增加代码的可靠性,也知道了如何将函数封装成头文件。遇到的困难有些已经解决,有些还需要继续深入研究。写这个程序我也没有用到特别复杂的算法,在与其他同学交流解题思路的过程中,我深刻的意识到了自己的不足以及与别人的差距。以后,我一定丰富自己的知识,将之前那些看不懂就放到一边的算法拾起来。
学习记录
问题
(1)vs2017无法查找或打开 pdb 文件
(2)单元测试方法1单元测试方法2
(3)C语言中 scanf_s和 scanf 区别
C++中函数strcpy和strcpy_s
(4)C如何在一个文件里调用另一个源文件中的函数
(5)struct重定义//重复包含头文件
(6)性能分析报告与性能优化
(7)看代码覆盖率使用OpenCppCoverage插件
(8)map学习