软件工程第二次作业

文章讲述了在软件工程课程中,学生需完成一个作业,涉及从JSON文件中提取数据,设计接口,使用nlohmann/json库解析数据,优化性能,编写单元测试,并处理异常。作业内容包括分析athletes.json和event.json,以及对参赛选手和比赛结果的处理。
摘要由CSDN通过智能技术生成
这个作业属于哪个课程2302软件工程社区
这个作业要求在哪里软件工程第二次作业
这个作业的目标完成世界游泳锦标赛官网的跳水项目数据搜集,并实现一个数据统计的 Console 程序
其他参考文献

Gitcode项目地址

https://gitcode.net/ozline/project-c

PSP 表格

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划1010
• Estimate估计这个任务需要多少时间1010
Development开发600410
• Analysis• 需求分析(包括学习新技术)12050
• Design Spec• 生成设计文档2020
• Design Review• 设计复审6020
• Coding Standard• 代码规范 (为目前的开发制定合适的规范)2020
• Design• 具体设计12020
• Coding• 具体编码360120
• Code Review• 代码复审12060
• Test• 测试(自我测试,修改代码,提交修改)120100
Reporting报告210100
• Test Repor• 测试报告8040
• Size Measurement• 计算工作量6040
• Postmortem & Process Improvement Plan• 事后总结, 并提出过程改进计划6020
合计820520

解题思路描述

问题1

对于问题一,我们需要实现参赛选手的信息读取,我们按以下顺序分析

  1. 分析athletes.json文件,找到我们需要提取的内容
  2. 构建项目基建
  3. 在项目基建的基础上,利用nlohmann/json库实现 json 解析
  4. 将输出文件输出到指定位置

问题2

问题二在问题一的基础上更加复杂了一些,它需要我们做如下内容

  1. 根据比赛文本,在event.json中找到指定的比赛 id
  2. 根据比赛 id,在results文件夹中找到指定的比赛具体信息
  3. 分析具体的信息,找到我们需要的 key
  4. 编写代码,整合信息并类似问题 1 的解决过程解决问题 2

接口设计和实现的过程

设计如下接口

#ifndef UTILITIES_H
#define UTILITIES_H

#include <vector>
#include "participant.h"
#include "result.h"
#include "nlohmann/json.hpp"
#include <cstring>

// 验证比赛名称合法性
bool checkNameVaility(const std::string& name);

// 输出 N/A 到文件中
void writeNAToFile(const std::string& filename);

// 输出 Error 到文件中
void writeErrorToFile(const std::string& filename);

// 读入参赛选手到向量中
void readParticipantsFromFile(const std::string& filename, std::vector<Participant>& participants);

// 从向量中输出参赛选手
void writeParticipantsToFile(const std::string& filename, const std::vector<Participant>& participants);

// 找到比赛 id
std::string findResultsId(const std::string& filename, const std::string& gender, const std::string& disciplineName);

// 读入比赛具体结果到向量中
void readEventResultFromFile(const std::string& filename, std::vector<EventResult>& eventResult);

// 输出比赛结果到向量中
void writeEventResultsToFile(const std::string& filename, const std::vector<EventResult>& eventResult);

#endif // UTILITIES_H

在这之后,我们单独设计participanteventResult两个类,这样封装起来具体的内容

#ifndef PARTICIPANT_H
#define PARTICIPANT_H

#include <string>

struct Participant {
    std::string fullName;
    std::string gender;
    std::string country;
};

std::string genderToString(int genderCode);

bool compareParticipants(const Participant& a, const Participant& b);

#endif // PARTICIPANT_H
#ifndef RESULT_H
#define RESULT_H

#include <string>

struct EventResult {
    std::string fullName;
    std::string rank;
    std::string score;
};

#endif // RESULT_H

关键代码展示

  • 校验比赛合法性
bool checkNameVaility(const std::string& name) {
    std::string list[10] = {  "women 1m springboard",
                            "women 3m springboard",
                            "women 10m platform",
                            "women 3m synchronised",
                            "women 10m synchronised",
                            "men 1m springboard",
                            "men 3m springboard",
                            "men 10m platform",
                            "men 3m synchronised",
                            "men 10m synchronised"
                        };

    for (const auto& str : list) {
        if (toLower(name) == toLower(str)) {
            return true;
        }
    }
    return false;
}
  • 查找比赛 id、根据 id 查找比赛详情、输出比赛详情
std::string findResultsId(const std::string& filename, const std::string& gender, const std::string& disciplineName) {
    std::ifstream input_file(filename);
    if (!input_file.is_open()) {
        ErrorHandle::print("无法打开文件: " + filename);
    }

    json events;
    input_file >> events;

    std::string res = "";

    for (const auto& sport : events["Sports"]) {
        for (const auto& discipline : sport["DisciplineList"]) {
            std::string jsonGender = toLower(discipline["Gender"].get<std::string>());
            std::string jsonDisciplineName = toLower(discipline["DisciplineName"].get<std::string>());
            if (jsonGender == gender && jsonDisciplineName == disciplineName) {
                res = discipline["Id"].get<std::string>();
                break;
            }
        }
        if (res != "") {
            break;
        }
    }

    input_file.close();
    return res;
}

void readEventResultFromFile(const std::string& filename, std::vector<EventResult>& eventResult) {
    std::ifstream input_file(filename);
    if (!input_file.is_open()) {
        ErrorHandle::print("无法打开文件: " + filename);
    }

    json data;
    input_file >> data;

    auto& heats = data["Heats"];
    auto& results = heats[0];
    for (const auto& result : results["Results"]) {
        std::string scores = "";
        std::string totalPoints = "";

        // 获取分数(相加形式)
        for (const auto& dive : result["Dives"]) {
            if (scores != "") scores += "+ ";
            scores += dive["DivePoints"].get<std::string>() + " ";
            totalPoints = dive["TotalPoints"].get<std::string>();
        }

        scores += "= " + totalPoints;

        // 推入结果
        eventResult.push_back({
            result["FullName"].get<std::string>(),
            std::to_string(result["Rank"].get<int>()),
            scores
        });
    }

    input_file.close();
}

void writeEventResultsToFile(const std::string& filename, const std::vector<EventResult>& eventResult) {
    std::ofstream output_file(filename, std::ios::app);
    if (!output_file.is_open()) {
        ErrorHandle::print("无法打开文件: " + filename);
    }

    for (const auto& result : eventResult) {
        output_file << "Full Name: " << result.fullName << std::endl;
        output_file << "Rank: " << result.rank << std::endl;
        output_file << "Score: " << result.score << std::endl;
        output_file << "-----" << std::endl;
    }

    output_file.close();
}

性能改进

  1. 我们使用ffstreamsstream两个库来改进传统的文件读取 IO 效率低的问题

  2. 在 json 解析部分,我们选择使用高性能的nlohmann/json库来实现 json 解析

  3. 在工程项目部分,我们使用 vcpkg 高性能包管理器来管理包

单元测试

src/tests中我编写了十个测试样例,以及十个正确的程序输出

我使用了脚本来自动化完成单元测试内容

在这里插入图片描述

异常处理

我们封装了一个error.herror.cpp一个简易的错误包装,并进行适当的输出

/**
 * @file error.cpp
 * @brief 错误封装
 * @author ozline
 * @date 2024-03-03
 *
 */
#include "error.h"
#include <iostream>
#include <cstdlib>

namespace ErrorHandle {
    void print(const std::string& message) {
        std::cerr << "Error: " << message << std::endl;
    }

}

这样可以保证输出的规范性

心得体会

从作业一到作业二,难点主要在于 json 的解析,单元测试的编写和性能提升。

细致来说,我本人第一次尝试使用 cpp 构建工程项目,CMake、Makefile 和 shell 脚本的学习也得到了进一步的加强

但是整个项目结构仍然有一定可优化的地方,期待未来更进一步的提升

  • 20
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OZLIINEX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值