软件工程实践第二次作业——个人实战

221900125_王凯达 学生 2022-03-04 02:31:58
这个作业属于哪个课程2022年福大-软件工程;软件工程实践-W班
这个作业要求在哪里软件工程实践第二次作业——个人实战
这个作业的目标完成对冬奥会的赛事数据的收集,
并实现一个能够对国家排名及奖牌个数统计的控制台程序
其他参考文献邹欣老师博客关于单元测试和回归测试
Gitcode-fork签入 项目克隆与提交
微软官方帮助文档
CSDN中的多篇文章

本文章中的爬取行为仅用于课程教学

目录

  • Gitcode项目地址
  • PSP表格
  • 解题思路描述
  • 1. 在编程前
  • 2. 实现基本功能
  • 3. 接口封装
  • 4. 单元测试和性能分析
  • 接口设计和实现过程
  • 1. 代码文件
  • 2. 函数设计
  • 3. 关键函数和函数关系流程图
  • 算法关键和独到之处
  • 关键代码展示及说明
  • 1. 针对不同输入指令的处理
  • 2. 从json文件中读取数据输出到output
  • 性能改进
  • 单元测试
  • 异常处理
  • 心得体会

Gitcode项目地址

这是一个可以点击的地址
这是地址链接:https://gitcode.net/Kedar1/personalproject-c

PSP表格

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划1616
• Estimate• 估计这个任务需要多少时间1616
Development开发31501630
• Analysis• 需求分析 (包括学习新技术)40369
• Design Spec• 生成设计文档50116
• Design Review• 设计复审1010
• Coding Standard• 代码规范 (为目前的开发制定合适的规范)5054
• Design• 具体设计6021
• Coding• 具体编码1880150
• Code Review• 代码复审6034
• Test• 测试(自我测试,修改代码,提交修改)1000876
Reporting报告390336
• Test Report• 测试报告6067
• Size Measurement• 计算工作量305
• Postmortem & Process Improvement Plan• 事后总结, 并提出过程改进计划300264
合计35561982

解题思路描述

1. 在编程前

  • 首先是对本次作业内容的分析,第一遍看下来是头皮发麻的,感觉自己好像什么都不懂,但是沉下心来在多看几遍就发现其实是可以循序渐进的做下去,在做的过程中慢慢学习。
  • 选C++的原因是因为可能我第一个接触的编译器是VS,在大部分同学都使用devC++的时候VS2019真的像神兵天降。虽然写C语言由于VS的SDL检查增添了很多的麻烦,但是C++就很少有这个困扰,而且我最喜欢的还是它idea过期了还没去申请 ,所以这次理所当然的选了C++!
  • 首先要弄懂的是gitcode的fork。虽然在上次的作业中使用了gitcode,但是对于git的使用等等依然不是很熟悉。在搜索之后学会了fork,但是在之后的提交过程中发现在fork下来的文件夹里居然不能提交!但是在上次的文件夹里随便写什么都可以提交!我百思不得其解还很纳闷,在经历了一两天的痛苦搜索解决不了之后,先开始放弃解决摆烂开始编写代码,在准备第一次commit时又去研究了研究,还是行不通,最后尝试从问题的根源入手把git卸载重装但是依旧失败。最后不抱希望的把fork下来的文件夹删了,重新克隆了一份,居然提交就成功了!不过虽然成功了,但是我还是很疑惑的在群里提出了问题,老师发了一篇文章:这才是真正的Git——Git内部原理揭秘!让我看看有没有帮助。我看完了之后感觉自己学到了很多有关于git的知识,但是这个问题还是存在疑问,我最后把它归结于软件奇奇怪怪的bug。

2. 实现基本功能

  • 在撰写完代码规范之后,就开始本次代码的编写。通过作业的要求,基本功能就是根据读取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格式转换成中文,去除奇怪的"\/"符号),成功获得需要的完美数据。

3. 接口封装

由于在大一下在wch老师的教授下学习(有乖乖听话),后面也有要求自己编程时注意代码可读性和结构,所以在编写时有进行了接口封装。

4. 单元测试和性能分析

VS2022中有相应的功能,在百度学习了大佬们的文章之后再自己上手,大概掌握了如何使用。

接口设计和实现过程

1. 代码文件

头文件有一个,为OlympicSearch.h(引用库、std命名空间和所有函数的函数定义)
共有九个函数

头文件

cpp文件一共有两个,分别为functions.cpp(头文件中所有函数的实现)和OlympicSearch.cpp(主程序,可以从命令行接收参数,作为程序入口)

2. 函数设计

  • 首先我先建立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++删除文件末尾的空行,感觉要是能帮上其他人的忙还是挺自豪的。

3. 关键函数和函数关系流程图

流程图

算法关键和独到之处

算法关键在于对各种输入的判断。独到之处我认为一是我在去除空格的问题上,通过对字符串的裁剪,针对不同指令使用一遍或两遍的首尾空格去除即解决这个问题;二是在去除换行中同时使用C++和C语言提升了性能。

关键代码展示及说明

1. 针对不同输入指令的处理

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

2. 从json文件中读取数据输出到output

由于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文件均相同

第一版为刚写完代码时的,下图为消耗时间

时间


下图为函数占用CPU情况

函数


下图为调用树情况

调用树

第一次的性能提升在我发现了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);
    }

input


json

心得体会

  • 高强度工作好累啊啊啊啊啊啊啊啊啊啊啊啊啊啊
  • 好像还是跟着大家一起用Java感觉好一点,周围认识的人,群里提问题的人基本就我一个人用C++都没地方交流问问题。
  • commit还是要完成一部分就提交一部分,不要一直攒着提交,会心慌达不到10次commit的要求,不过最后还是在各种修改中超越了
  • 需求一定要认真看!需求一定要认真看!需求一定要认真看!重要的事情说三遍,不然看错了再改真的很苦
  • 英语要好好学习,想起那个因为schedule在txt中打成schdule出现的让我百思不得其解的错误输出真的很无语
  • 平时要多看资料多学习,不然装什么包啦开源库啦每一项都比代码更折磨人
  • 这是第一次使用PSP表格,在刚好配上番茄自习的计时,感觉还是很惊讶神奇的。刚开始以为代码编写要写好久好久,没想到只有预估的十分之一,但是测试确实实实在在的要逼近了预计值,还是很惊讶测试的时间消耗居然是代码编写的六倍左右。还有需求分析也是代码的两倍,是我预估计时间的九倍,还是很可怕的。
...全文
306 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

142

社区成员

发帖
与我相关
我的任务
社区描述
2022年福大-软件工程;软件工程实践-W班
软件工程 高校
社区管理员
  • FZU_SE_teacherW
  • 丝雨_xrc
  • Lyu-
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧