Loading

软件开发与创新课程设计 第七周作业


一、报告概述

  本次结对编程围绕“简易在线考试系统”展开,采用C++语言开发,实现题目增删改查、文件持久化、随机出题考试、成绩统计等核心功能。结对编程两人协同合作,遵循“分工明确、优势互补、高效协作”的原则,完成项目从需求分析到测试部署的全流程开发,既保证了代码质量,又提升了开发效率,同时积累了结对协作的实践经验。本报告将详细记录结对编程的成员分工、代码、遇到的问题及解决方法、项目成果与总结展望。




二、结对成员及分工

  本次结对编程共2名成员,基于项目模块低耦合、高内聚的原则,结合两人技术特长进行合理分工,明确各自职责与协作边界,确保开发过程有序推进。具体分工如下:

成员1 (2452330 崔珈铭 博客园地址)

  核心职责:负责项目基础数据结构、工具函数、题目管理模块及文件持久化模块的开发与优化,搭建系统的数据层与基础工具支撑,确保题目数据的安全存储与高效管理。

  ①定义Question结构体,封装题目ID、题干、题型、选项、正确答案索引等核心字段,实现typeStr()成员函数,用于返回题型描述,保障数据结构的合理性与易用性。

  ②开发文件持久化相关函数,包括saveQuestions()(将题库数据写入questions.txt文件)loadQuestions()(从文件加载题库数据),处理文件打开失败等异常情况,保证数据持久化的可靠性。

  ③实现基础工具函数,包括waitForEnter()(等待用户按回车继续)clearInput()(清空输入缓冲区)displayQuestion()(展示题目详情,用于管理界面),提升用户交互体验与代码复用性。

  ④开发题目管理核心功能,包括addQuestion()(新增题目,区分单选题与判断题的差异化逻辑)listQuestions()(查看所有题目)updateQuestion()(修改题目信息)deleteQuestion()(删除指定ID题目),完善输入合法性校验,确保功能的健壮性。

  ⑤配合成员2完成模块联调,提供清晰的题目数据调用接口,验证题目数据能被考试模块正确读取与使用。函数,用于返回题型描述,保障数据结构的合理性与易用性。

成员2 (2452419 宗雨辰 博客园地址)

  核心职责:负责考试核心逻辑、用户交互流程、主程序整合及系统测试调试,搭建系统的业务流程层与交互层,确保考试流程顺畅、用户体验良好。

  ①开发shuffleOptions()函数,实现题目选项的随机打乱功能,同时匹配打乱后正确答案的索引,确保考试的公平性。

  ②实现startExam()函数,完成考试全流程开发,包括题目展示(打乱后选项)、用户答案输入校验、计分规则、错误答案提示、成绩统计与展示,处理题库为空等边界场景。

  ③负责main()函数的开发与优化,设计系统主菜单,实现菜单循环展示、用户选择接收、对应功能调用的逻辑,优化菜单提示语与错误输入提示,提升用户交互体验。

  ④完成系统联调,整合成员1开发的题目管理模块,验证“新增题目→加载题目→开始考试”的端到端流程,确保各模块衔接顺畅。

  ⑤负责系统测试与调试,排查功能漏洞(如输入非法字符、题型修改后数据异常等),优化代码容错性与运行效率。




三、代码模块

文件持久化saveQuestions()
void saveQuestions()
{
    ofstream out("questions.txt");
    if (!out)
    {
        cerr << "警告:无法保存题目文件!\n";
        return;
    }
    out << questions.size() << "\n";
    for (const auto &q : questions)
    {
        out << q.id << "|" << q.type << "|" << q.text << "|";
        // 保存选项,用逗号分隔
        for (size_t i = 0; i < q.options.size(); ++i)
        {
            if (i > 0)
                out << ",";
            out << q.options[i];
        }
        out << "|" << q.correctIndex << "\n";
    }
    out.close();
}
加载问题loadQuestions()
void loadQuestions()
{
    ifstream in("questions.txt");
    if (!in)
        return; // 没有旧文件,直接开始

    questions.clear();
    size_t count;
    in >> count;
    in.ignore(); // 忽略换行
    for (size_t i = 0; i < count; ++i)
    {
        string line;
        getline(in, line);
        if (line.empty())
            continue;
        stringstream ss(line);
        string idStr, typeStr, text, optionsStr, correctStr;
        getline(ss, idStr, '|');
        getline(ss, typeStr, '|');
        getline(ss, text, '|');
        getline(ss, optionsStr, '|');
        getline(ss, correctStr, '|');

        Question q;
        q.id = stoi(idStr);
        q.type = stoi(typeStr);
        q.text = text;
        // 解析选项
        stringstream optStream(optionsStr);
        string opt;
        while (getline(optStream, opt, ','))
        {
            q.options.push_back(opt);
        }
        q.correctIndex = stoi(correctStr);

        questions.push_back(q);
        if (q.id >= nextId)
            nextId = q.id + 1;
    }
    in.close();
}
清除缓冲区clearInput()
void clearInput()
{
    cin.clear();
    cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
显示单个题目displayQuestion()
void displayQuestion(const Question &q, int index = -1)
{
    if (index >= 0)
        cout << "[" << index << "] ";
    cout << "ID:" << q.id << " | " << q.typeStr() << " | " << q.text << "\n";
    char optLetter = 'A';
    for (size_t i = 0; i < q.options.size(); ++i)
    {
        cout << "   " << char(optLetter + i) << ". " << q.options[i] << "\n";
    }
    cout << "   正确答案: " << char('A' + q.correctIndex) << "\n";
}
添加题目addQuestion()
void addQuestion()
{
    Question q;
    q.id = nextId++;
    cout << "\n--- 新增题目 ---\n";
    cout << "请输入题干:(先输入回车再输入题目) \n";
    clearInput();
    getline(cin, q.text);

    cout << "题型 (0-单选题, 1-判断题): ";
    int t;
    cin >> t;
    while (t != 0 && t != 1)
    {
        cout << "输入错误,请重新输入 (0/1): ";
        cin >> t;
    }
    q.type = t;
    clearInput();

    if (q.type == 0)
    { // 单选题
        int optCount;
        cout << "请输入选项个数 (2-6): ";
        cin >> optCount;
        while (optCount < 2 || optCount > 6)
        {
            cout << "选项个数应为2~6,请重新输入: ";
            cin >> optCount;
        }
        clearInput();
        q.options.resize(optCount);
        for (int i = 0; i < optCount; ++i)
        {
            cout << "请输入选项 " << char('A' + i) << ": ";
            getline(cin, q.options[i]);
        }
        char correct;
        cout << "请输入正确选项 (A-" << char('A' + optCount - 1) << "): ";
        cin >> correct;
        correct = toupper(correct);
        int idx = correct - 'A';
        while (idx < 0 || idx >= optCount)
        {
            cout << "无效选项,请重新输入: ";
            cin >> correct;
            correct = toupper(correct);
            idx = correct - 'A';
        }
        q.correctIndex = idx;
    }
    else
    { // 判断题
        q.options = {"正确", "错误"};
        int correctVal;
        cout << "请输入正确答案 (1-正确, 2-错误): ";
        cin >> correctVal;
        while (correctVal != 1 && correctVal != 2)
        {
            cout << "请输入1或2: ";
            cin >> correctVal;
        }
        q.correctIndex = (correctVal == 1) ? 0 : 1;
    }

    questions.push_back(q);
    saveQuestions();
    cout << "题目添加成功!\n";
    waitForEnter();
}
查看所有题目listQuestions()
void listQuestions()
{
    if (questions.empty())
    {
        cout << "\n题库为空。\n";
        waitForEnter();
        return;
    }
    cout << "\n========== 所有题目 ==========\n";
    for (size_t i = 0; i < questions.size(); ++i)
    {
        displayQuestion(questions[i], i + 1);
        cout << "----------------------------\n";
    }
    waitForEnter();
}
修改题目updateQuestion()
void updateQuestion()
{
    if (questions.empty())
    {
        cout << "\n题库为空,无法修改。\n";
        waitForEnter();
        return;
    }
    int id;
    cout << "\n请输入要修改的题目ID: ";
    cin >> id;
    clearInput();

    auto it = find_if(questions.begin(), questions.end(), [id](const Question &q)
                      { return q.id == id; });
    if (it == questions.end())
    {
        cout << "未找到该ID的题目。\n";
        waitForEnter();
        return;
    }

    cout << "当前题目信息:\n";
    displayQuestion(*it);
    cout << "\n开始修改(直接回车保留原值)\n";

    string newText;
    cout << "新题干(原:" << it->text << "): ";
    getline(cin, newText);
    if (!newText.empty())
        it->text = newText;

    int newType;
    cout << "新题型(0-单选 1-判断,原:" << it->type << "): ";
    string typeStr;
    getline(cin, typeStr);
    if (!typeStr.empty())
    {
        newType = stoi(typeStr);
        if (newType != it->type)
        {
            // 题型改变,需要重置选项
            it->type = newType;
            if (newType == 1)
            { // 改成判断题
                it->options = {"正确", "错误"};
                int correctVal;
                cout << "请输入正确答案 (1-正确, 2-错误): ";
                cin >> correctVal;
                clearInput();
                it->correctIndex = (correctVal == 1) ? 0 : 1;
            }
            else
            { // 改成单选题
                int optCount;
                cout << "请输入选项个数 (2-6): ";
                cin >> optCount;
                clearInput();
                it->options.resize(optCount);
                for (int i = 0; i < optCount; ++i)
                {
                    cout << "选项 " << char('A' + i) << ": ";
                    getline(cin, it->options[i]);
                }
                char correct;
                cout << "正确选项 (A-" << char('A' + optCount - 1) << "): ";
                cin >> correct;
                clearInput();
                it->correctIndex = toupper(correct) - 'A';
            }
        }
    }

    // 如果题型未变且是单选题,可以修改选项和答案
    if (it->type == 0 && typeStr.empty())
    {
        cout << "是否修改选项?(y/n): ";
        char modifyOpt;
        cin >> modifyOpt;
        clearInput();
        if (modifyOpt == 'y' || modifyOpt == 'Y')
        {
            int optCount;
            cout << "新选项个数 (2-6): ";
            cin >> optCount;
            clearInput();
            it->options.resize(optCount);
            for (int i = 0; i < optCount; ++i)
            {
                cout << "选项 " << char('A' + i) << ": ";
                getline(cin, it->options[i]);
            }
            char correct;
            cout << "新正确选项: ";
            cin >> correct;
            clearInput();
            it->correctIndex = toupper(correct) - 'A';
        }
    }

    saveQuestions();
    cout << "修改成功!\n";
    waitForEnter();
}
删除题目deleteQuestion()
void deleteQuestion()
{
    if (questions.empty())
    {
        cout << "\n题库为空。\n";
        waitForEnter();
        return;
    }
    int id;
    cout << "\n请输入要删除的题目ID: ";
    cin >> id;
    clearInput();

    auto it = find_if(questions.begin(), questions.end(), [id](const Question &q)
                      { return q.id == id; });
    if (it == questions.end())
    {
        cout << "未找到该ID的题目。\n";
    }
    else
    {
        questions.erase(it);
        saveQuestions();
        cout << "题目已删除。\n";
    }
    waitForEnter();
}
考试模块startExam()
void startExam()
{
    if (questions.empty())
    {
        cout << "\n题库为空,无法开始考试。\n";
        waitForEnter();
        return;
    }

    cout << "\n========== 开始考试 ==========\n";
    cout << "共 " << questions.size() << " 题,每题1分。\n";
    cout << "注意:选项顺序已经随机打乱,请仔细选择!\n";
    cout << "输入对应选项字母(A/B/C...)后按回车。\n\n";

    int score = 0;
    int total = questions.size();

    // 逐题作答
    for (size_t idx = 0; idx < questions.size(); ++idx)
    {
        const Question &q = questions[idx];

        // 随机打乱选项顺序(对单选题和判断题均生效)
        vector<string> shuffledOpts;
        int newCorrectIdx;
        shuffleOptions(q, shuffledOpts, newCorrectIdx);

        // 显示题目(打乱后的选项)
        cout << "第 " << idx + 1 << " 题:" << q.text << "\n";
        char optLetter = 'A';
        for (size_t i = 0; i < shuffledOpts.size(); ++i)
        {
            cout << "   " << char(optLetter + i) << ". " << shuffledOpts[i] << "\n";
        }

        // 获取用户输入
        char userChoice;
        cout << "你的答案: ";
        cin >> userChoice;
        userChoice = toupper(userChoice);
        int userIdx = userChoice - 'A';

        // 验证输入合法性
        while (userIdx < 0 || userIdx >= (int)shuffledOpts.size())
        {
            cout << "无效输入,请输入 A-" << char('A' + shuffledOpts.size() - 1) << ": ";
            cin >> userChoice;
            userChoice = toupper(userChoice);
            userIdx = userChoice - 'A';
        }

        // 判分
        if (userIdx == newCorrectIdx)
        {
            cout << " 正确\n\n";
            score++;
        }
        else
        {
            // 显示正确答案(选项文本)
            string correctText = shuffledOpts[newCorrectIdx];
            cout << " 错误。正确答案是 " << char('A' + newCorrectIdx) << ". " << correctText << "\n\n";
        }
    }

    // 输出最终成绩
    cout << "========== 考试结束 ==========\n";
    cout << "你的得分: " << score << " / " << total << "\n";
    double percent = (total > 0) ? (score * 100.0 / total) : 0;
    cout << "正确率: " << fixed << setprecision(1) << percent << "%\n";

    waitForEnter();
}
主菜单main()
int main()
{
    loadQuestions(); // 加载已有题目

    int choice;
    do
    {
        cout << "\n====== 简易在线考试系统 ======\n";
        cout << "1. 添加题目\n";
        cout << "2. 查看所有题目\n";
        cout << "3. 修改题目\n";
        cout << "4. 删除题目\n";
        cout << "5. 开始考试\n";
        cout << "6. 退出系统\n";
        cout << "请选择 (1-6): ";
        cin >> choice;
        clearInput();

        switch (choice)
        {
        case 1:
            addQuestion();
            break;
        case 2:
            listQuestions();
            break;
        case 3:
            updateQuestion();
            break;
        case 4:
            deleteQuestion();
            break;
        case 5:
            startExam();
            break;
        case 6:
            cout << "谢谢使用!\n";
            break;
        default:
            cout << "无效选项,请重新输入。\n";
            waitForEnter();
            break;
        }
    } while (choice != 6);

    return 0;
}




四、项目成果

  经过结对编程协作,成功完成简易在线考试系统的开发,实现了所有核心功能,达到了预期需求,具体成果如下:

  • 功能完整:实现了题目增删改查、文件持久化、随机打乱选项考试、成绩统计与展示等核心功能,支持单选题与判断题两种题型,满足基本的在线考试需求。

  • 代码规范:代码命名、注释符合约定规范,结构清晰,逻辑严谨,无语法错误与明显漏洞,可维护性强。

  • 用户体验良好:具备完善的输入校验与友好的提示信息,考试过程中选项随机打乱,保证公平性,成绩展示清晰,操作流程简洁易懂。

  • 运行稳定:经过多轮测试,系统能够稳定运行,无崩溃、数据丢失等问题,文件持久化功能可靠,题目数据能够正常保存与加载。

主菜单 添加题目 查看所有题目

主菜单

1

2

修改题目 删除题目 开始考试

3

4

5

  项目最终交付文件包括:project1.cpp(核心源代码)questions.txt(题库数据文件),以及本结对编程报告,完整呈现了项目开发的全过程。




五、问题及解决方法

  问题1:题目选项打乱后,正确答案索引无法匹配,导致考试判分错误。

  解决方法:两人共同分析逻辑,确定通过保存原正确选项的文本,在打乱后的选项列表中查找该文本的位置,从而获取新的正确答案索引,修改shuffleOptions()函数的逻辑,测试后问题解决。

void shuffleOptions(const Question &q, vector<string> &shuffledOpts, int &newCorrectIdx)
{
    shuffledOpts = q.options;
    // 使用随机洗牌
    shuffle(shuffledOpts.begin(), shuffledOpts.end(), rng);
    // 找到原正确选项的文本,在新列表中的位置
    string correctText = q.options[q.correctIndex];
    for (size_t i = 0; i < shuffledOpts.size(); ++i)
    {
        if (shuffledOpts[i] == correctText)
        {
            newCorrectIdx = i;
            break;
        }
    }
}

  问题2:修改题目题型时,若从单选题改为判断题,选项已重置为“正确”“错误”,但正确答案索引未同步更新,导致后续考试判分异常。

  解决方法:成员1修改updateQuestion()函数,在题型切换为判断题时,强制要求用户重新输入正确答案,同步更新正确答案索引,确保数据一致性。

if (!typeStr.empty())
    {
        newType = stoi(typeStr);
        if (newType != it->type)
        {
            // 题型改变,需要重置选项
            it->type = newType;
            if (newType == 1)
            { // 改成判断题
                it->options = {"正确", "错误"};
                int correctVal;
                cout << "请输入正确答案 (1-正确, 2-错误): ";
                cin >> correctVal;
                clearInput();
                it->correctIndex = (correctVal == 1) ? 0 : 1;
            }
            else
            { // 改成单选题
                int optCount;
                cout << "请输入选项个数 (2-6): ";
                cin >> optCount;
                clearInput();
                it->options.resize(optCount);
                for (int i = 0; i < optCount; ++i)
                {
                    cout << "选项 " << char('A' + i) << ": ";
                    getline(cin, it->options[i]);
                }
                char correct;
                cout << "正确选项 (A-" << char('A' + optCount - 1) << "): ";
                cin >> correct;
                clearInput();
                it->correctIndex = toupper(correct) - 'A';
            }
        }
    }

    // 如果题型未变且是单选题,可以修改选项和答案
    if (it->type == 0 && typeStr.empty())
    {
        cout << "是否修改选项?(y/n): ";
        char modifyOpt;
        cin >> modifyOpt;
        clearInput();
        if (modifyOpt == 'y' || modifyOpt == 'Y')
        {
            int optCount;
            cout << "新选项个数 (2-6): ";
            cin >> optCount;
            clearInput();
            it->options.resize(optCount);
            for (int i = 0; i < optCount; ++i)
            {
                cout << "选项 " << char('A' + i) << ": ";
                getline(cin, it->options[i]);
            }
            char correct;
            cout << "新正确选项: ";
            cin >> correct;
            clearInput();
            it->correctIndex = toupper(correct) - 'A';
        }
    }

  问题3:文件加载时,若questions.txt文件中存在空行,会导致getline读取空行后解析失败,程序异常。

  解决方法:成员1loadQuestions()函数中添加空行判断,遇到空行直接跳过,避免解析异常,同时优化文件写入逻辑,确保写入的文件无多余空行。




六、总结与展望

  本次结对编程圆满完成了简易在线考试系统的开发任务,通过两人的协同合作,不仅高效完成了项目开发,还积累了宝贵的结对协作经验。相比单人开发,结对编程具有明显优势:导航员的审核机制有效降低了代码错误率,实时沟通减少了问题积累,分工协作提升了开发效率,互相学习促进了个人技术提升。
  同时,也认识到存在的不足:一是协作初期节奏把控不够好,导致部分代码规范不统一;二是对边界场景的考虑不够全面,测试阶段才发现部分异常问题;三是代码的扩展性有待提升,后续难以快速添加新功能(如多选题、批量导入题目等)。

posted @ 2026-04-16 18:40  Willakalabb  阅读(14)  评论(0)    收藏  举报