142
社区成员
发帖
与我相关
我的任务
分享| 这个作业属于哪个课程 | 2022年福大-软件工程;软件工程实践-W班 |
|---|---|
| 这个作业要求在哪里 | 软件工程实践第二次作业——个人实战 |
| 这个作业的目标 | 完成对冬奥会的赛事数据的收集, 并实现一个能够对国家排名及奖牌个数统计的控制台程序 |
| 其他参考文献 | 邹欣老师博客关于单元测试和回归测试 Gitcode-fork签入 项目克隆与提交 微软官方帮助文档 CSDN中的多篇文章 |
本文章中的爬取行为仅用于课程教学
这是一个可以点击的地址
这是地址链接:https://gitcode.net/Kedar1/personalproject-c
| PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 16 | 16 |
| • Estimate | • 估计这个任务需要多少时间 | 16 | 16 |
| Development | 开发 | 3150 | 1630 |
| • Analysis | • 需求分析 (包括学习新技术) | 40 | 369 |
| • Design Spec | • 生成设计文档 | 50 | 116 |
| • Design Review | • 设计复审 | 10 | 10 |
| • Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 50 | 54 |
| • Design | • 具体设计 | 60 | 21 |
| • Coding | • 具体编码 | 1880 | 150 |
| • Code Review | • 代码复审 | 60 | 34 |
| • Test | • 测试(自我测试,修改代码,提交修改) | 1000 | 876 |
| Reporting | 报告 | 390 | 336 |
| • Test Report | • 测试报告 | 60 | 67 |
| • Size Measurement | • 计算工作量 | 30 | 5 |
| • Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 300 | 264 |
| 合计 | 3556 | 1982 |
- 首先是对本次作业内容的分析,第一遍看下来是头皮发麻的,感觉自己好像什么都不懂,但是沉下心来在多看几遍就发现其实是可以循序渐进的做下去,在做的过程中慢慢学习。
- 选C++的原因是因为可能我第一个接触的编译器是VS,在大部分同学都使用devC++的时候VS2019真的像神兵天降。虽然写C语言由于VS的SDL检查增添了很多的麻烦,但是C++就很少有这个困扰,而且我最喜欢的还是它
idea过期了还没去申请,所以这次理所当然的选了C++!- 首先要弄懂的是gitcode的fork。虽然在上次的作业中使用了gitcode,但是对于git的使用等等依然不是很熟悉。在搜索之后学会了fork,但是在之后的提交过程中发现在fork下来的文件夹里居然不能提交!但是在上次的文件夹里随便写什么都可以提交!我百思不得其解还很纳闷,在经历了一两天的痛苦搜索解决不了之后,先
开始放弃解决摆烂开始编写代码,在准备第一次commit时又去研究了研究,还是行不通,最后尝试从问题的根源入手把git卸载重装但是依旧失败。最后不抱希望的把fork下来的文件夹删了,重新克隆了一份,居然提交就成功了!不过虽然成功了,但是我还是很疑惑的在群里提出了问题,老师发了一篇文章:这才是真正的Git——Git内部原理揭秘!让我看看有没有帮助。我看完了之后感觉自己学到了很多有关于git的知识,但是这个问题还是存在疑问,我最后把它归结于软件奇奇怪怪的bug。
- 在撰写完代码规范之后,就开始本次代码的编写。通过作业的要求,基本功能就是根据读取input文件中两种不同的指令,然后程序读取data中的文件并输出到output中。这时候就要进行对json文件的读取,虽然git折磨了我很久,但是刚好磨到了助教老师宣布可以使用第三方的json库的时候,于是很爽快地决定使用json-c。
- 然而这个比git还折磨人,网上对于VS安装使用json的甚少,很多资料让我无从下手,这时候扼腕叹息该用Java的。但是我又很不很甘心,所以又花了两三天,单独用json的压缩包导入失败不能使用json.h,最后装了开源库集成器vcpkg使用这个去下载安装编译json包。但是由于众所周知的原因,从Github上clone的速度已经很抽象了而vcpkg在PowerShell中的下载速度更是让人吐血,还会下载一半突然报错。最后只能使用魔法把安装编译json需要的各种包插件什么的下载下来,再在放进vcpkg中安装编译。
- 不过安装编译很痛苦,用上json库编写的过程就很顺利了,一个功能接着一个功能的实现。
- 爬取15号之后的数据因为之前没有过经验,在各种搜索下依然很茫然所以求助了舍友,伟大的舍友教会了我用F12去获取网站上需要的信息,最后再对数据进行处理(比如json文件在VSCode中格式化,将Unicode格式转换成中文,去除奇怪的"\/"符号),成功获得需要的完美数据。
由于在大一下在wch老师的教授下学习(有乖乖听话),后面也有要求自己编程时注意代码可读性和结构,所以在编写时有进行了接口封装。
VS2022中有相应的功能,在百度学习了大佬们的文章之后再自己上手,大概掌握了如何使用。
头文件有一个,为OlympicSearch.h(引用库、std命名空间和所有函数的函数定义)
共有九个函数

cpp文件一共有两个,分别为functions.cpp(头文件中所有函数的实现)和OlympicSearch.cpp(主程序,可以从命令行接收参数,作为程序入口)
- 首先我先建立inputtxt_deal()函数用于处理input文件中的输出指令,一开始暂时先不考虑指令输入是否正确,只分成total和schedule两种情况,然后编写函数instructionTotal_output()用于total指令对应的输出,编写函数instructionConcrete_output(string instruction)用作指令为其他具体指令的输出。
- 在输入输出完成后,我开始进行了错误指令的处理。除去一些简短的选择分支语句以外,我编写了函数is_errorDate(string behind)作为日期错误的判断,errorInsturction_deal(string instruction)对错误指令判断和处理。在对错误指令处理时,我发现有很多不同分支中输出N/A或者Error的情况,所以我把输出Error的代码封装为onlyError_write(),输出N/A的代码封装为onlyNA_write()。
- 然后是处理空格,我想到了一个好主意就是我先进行一次首尾空格的去除,再解决掉total的判断,当判断前面有“schedule”时就剪掉字符串的前八位,然后再进行一次首尾空格的去除,就可以解决掉schedule和日期中间有很多空格的问题。首尾空格去除封装为函数correctBlank_delete(string instruction)。
- 由于指令在循环中不停读取可能产生新的输出,所以最后去掉output文件末尾的换行放在每次的输出并不恰当,所以放在所有指令输出完毕之后,进行换行的去除,保证输出的无误。在那天需求在群里提出之后,大佬许同学发了一张Java的实现图,我看了一眼就四五行代码看着很简单,结果在C++中没想到是个极为复杂的问题,因为在C++中并没有可以修改文件大小或者裁剪文件的函数,最后经过一段时间的尝试和学习,最终采取了C++和C语言混用的方式(为什么混用具体在性能测试位置解释),封装为函数lastLine_delete()。
- 因为C++去除output文件末尾换行的方法资料极少且很乱,部分混有unix系统和linux系统的方法,导致当时十分的痛苦。为了不让以后的其他人再经历这份痛苦,所以我发了一篇文章C++删除文件末尾的空行,感觉要是能帮上其他人的忙还是挺自豪的。

算法关键在于对各种输入的判断。独到之处我认为一是我在去除空格的问题上,通过对字符串的裁剪,针对不同指令使用一遍或两遍的首尾空格去除即解决这个问题;二是在去除换行中同时使用C++和C语言提升了性能。
while (!inTxt.eof())
{
getline(inTxt, instruction);
instruction = correctBlank_delete(instruction);//去除掉首尾空格
if (instruction.empty())//遇到空行跳过
continue;
else
{
if (instruction == "total")
instructionTotal_output();//指令为total
else if (instruction.size() < 5)//指令小于五位
onlyError_write();
else if (instruction.substr(0, 5) == "total" && instruction.size() > 5)//指令前五位为total但后面还有数据的情况
onlyError_write();
else
errorInsturction_deal(instruction);//其他指令
}
}
void errorInsturction_deal(string instruction)//错误指令处理
{
string font, behind;
if (instruction.size() < 8)//不为total且数据比schedule短
onlyError_write();
else {
font = instruction.substr(0, 8);
if (font != "schedule")//前8位不为schedule的情况
onlyError_write();
else if (instruction.length() == 8)//只有schedule的情况
onlyNA_write();
else if (instruction[8] != ' ')//schedule日期之间不为空格的情况
onlyError_write();
else {
behind = instruction.substr(8);//去除掉schedule
behind = correctBlank_delete(behind);//去除掉首尾得空格
if (behind.size() != 4)//位数不对的情况
onlyNA_write();
else
{
if (is_errorDate(behind) == "N/A")
onlyNA_write();
else
instructionConcrete_output(behind);
}
}
}
}
由于total和schedule的json读取输出差别并不是很大,在此只列出schedule的部分
if (reader.parse(json_file, root))
{
for (int i = 0; i < root["data"]["total"].asInt(); i++)
{
outTxt << "time:" << root["data"]["matchList"][i]["startdatecn"].asString().substr(11, 5) << "\n";
outTxt << "sport:" << root["data"]["matchList"][i]["itemcodename"].asString() << "\n";
outTxt << "name:" << root["data"]["matchList"][i]["title"].asString();
if (root["data"]["matchList"][i]["homename"].asString().empty() || root["data"]["matchList"][i]["awayname"].asString().empty())
outTxt << "\n";
else
outTxt << " " << root["data"]["matchList"][i]["homename"].asString() <<"VS"<< root["data"]["matchList"][i]["awayname"].asString() << "\n";
outTxt << "venue:" << root["data"]["matchList"][i]["venuename"].asString() << "\n";
outTxt << "-----" << "\n";
}
}
以下用于性能测评的input.txt文件均相同
第一版为刚写完代码时的,下图为消耗时间



第一次的性能提升在我发现了IO十分耗时,在对代码的研究后,封装了输出N/A和Error的方法以减少正确情况时打开IO的操作



第二次性能提升,在去除文档末尾换行时本来打算直接使用C语言打开,减少一次C++的IO操作,但是和我预估的不同,修改之后反而时间比尚未第一次性能提升之前还久。我研究了一下原因,发现问题出在去除C++之后,使用C语言读取文章大小使用的fseek和ftell函数太慢了,耗时令人发指,于是最后还是采取了C语言C++混用的方式。
因为第二次的性能提升失败了所以就不贴第二次的图了。
- 在前面的性能提升之后,我发现我的代码没有可以更改的地方了,能封装的都封装能减少的都减少了。在VS给的性能图中很清晰的可以看到,占用CPU最多是Json库中的Json::Reader,即读取json文件。但是我也没想到其他办法来取代,所以也就没有了再性能提升的空间,希望能收到老师和助教给予我的点拨。
- VS的单元测试查看代码覆盖率居然需要企业版和专业版,作为一个只用得起免费的社区版的学生,想要看代码覆盖率只能下次写代码选Java用idea测试了
很奇怪的是VS在测试的时候,在图中我也是一起测试的,但是只显示这个测试类通过了测试,我也不知道是我用的不好还是它就这个样子,看了看其他同学的idea测试页面,高大上啊

单元测试除了两个返回类型为string的函数比较特别,其他的函数都是比对output.txt和自己预计的储存在test.txt中的内容进行对比,所以这里只列出两个特别的函数和其它函数随便一个内容对比的测试代码。
string date = "0228";
string result = "N/A";
Assert::AreEqual(result, is_errorDate(date));
string numBlank = " 015 12";
string num = "01512";
Assert::AreEqual(num, correctBlank_delete(numBlank));
instructionTotal_output();
fstream outTxt, test;
outTxt.open("../OlympicSearch/output.txt");
test.open("../OlympicSearch/test.txt");
string out, ts;
while (!outTxt.eof())
out += outTxt.get();
while (!test.eof())
ts += test.get();
Assert::AreEqual(out, ts);
outTxt.close();
test.close();
这次代码编写中基本没有需要异常处理的地方(如果错误指令输入不算的话),然后错误指令的处理上面也有,所以这边就列出在IO中需要用到的异常处理。
if (!inTxt.is_open())//文件打开失败异常处理
{
cout << "input文件打开失败!";
exit(1);
}
if (!json_file.is_open())//文件打开失败异常处理
{
cout << "json文件打开失败!";
exit(1);
}
if (!outTxt.is_open())//文件打开失败异常处理
{
cout << "output文件打开失败!";
exit(1);
}

