结对编程

结对编程

目录

  1. 系统简介
  2. 核心功能
  3. 小组成员
  4. 代码
  5. 算法设计思路
  6. 运行结果展示
  7. 结对编程体会

一、系统简介

本系统为中学语文在线考试系统,基于C++语言实现,面向管理员与考生两类用户,提供题库管理、在线考试、自动判分、成绩存档、考试进度断点续考等完整功能。系统采用文件持久化存储数据,适用于课堂小测、课后练习等轻量化考试场景。


二、核心功能

  1. 管理员题库管理:
    管理员通过密码验证后,可对语文题目进行添加、修改、删除、查看,支持单选题、多选题、判断题三种题型,可设置题干、选项、标准答案、分值、题目解析。
  2. 考生在线考试:
    考生可直接进入考试,系统支持10 分钟限时作答,答题过程实时保存进度,支持中途退出后断点续考,也可手动提前交卷或查看剩余时间。
  3. 自动判分与错题编辑:
    交卷后系统自动批改,计算总分与正确率,展示所有错题及对应解析,方便考生查漏补缺。
  4. 成绩历史记录:
    自动保存每次考试的考生姓名、考试时间、得分与满分,支持查看历史成绩列表。
  5. 数据持久化:
    题库、考试进度、历史成绩均以二进制文件形式保存,程序重启数据不丢失。

三、小组成员

2452802
2452808


四、代码

点击查看代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

#define MAX_QUESTIONS 100
#define MAX_OPTION_LEN 100
#define MAX_TEXT_LEN 256
#define MAX_ANSWER_LEN 10
#define MAX_EXPLANATION_LEN 200
#define EXAM_DURATION_SEC 600   // 考试时长:10分钟(600秒)
#define PROGRESS_FILE "exam_progress.dat"
#define SCORE_FILE "scores.dat"

// 题目类型
#define TYPE_SINGLE 1
#define TYPE_MULTI 2
#define TYPE_JUDGE 3

// 题目结构体
typedef struct {
    int id;                     // 题目编号
    int type;                   // 类型:1单选 2多选 3判断
    char text[MAX_TEXT_LEN];    // 题干
    char options[4][MAX_OPTION_LEN]; // 选项(判断题忽略)
    char answer[MAX_ANSWER_LEN]; // 标准答案(单选如"A",多选如"ABD",判断"T"/"F")
    float score;                // 分值
    char explanation[MAX_EXPLANATION_LEN]; // 解析
} Question;

// 考试进度结构体(用于实时保存答案)
typedef struct {
    int totalQuestions;                 // 总题目数
    int currentIndex;                   // 下一题索引(0~totalQuestions)
    time_t startTime;                   // 考试开始时间戳
    int durationSec;                    // 考试时长(秒)
    char answers[MAX_QUESTIONS][MAX_ANSWER_LEN]; // 每道题的答案
} ExamProgress;

// 成绩记录结构体
typedef struct {
    char name[50];
    time_t examTime;
    float totalScore;
    float fullScore;
} ScoreRecord;

Question g_questions[MAX_QUESTIONS];
int g_questionCount = 0;

// 函数声明
void clearScreen();
void loadQuestions();
void saveQuestions();
void adminMenu();
void addQuestion();
void editQuestion();
void deleteQuestion();
void listQuestions();
void examMenu();
void startExam();
void calculateAndReport(ExamProgress *progress, const char *studentName);
int compareAnswer(const char *userAns, const char *stdAns, int type);
void saveProgress(const ExamProgress *progress);
int loadProgress(ExamProgress *progress);
void deleteProgress();
void saveScoreRecord(const char *name, float score, float fullScore);
void showScoreHistory();
void initSampleQuestions();

// 工具函数:去除字符串首尾空格和换行
void trim(char *str) {
    char *start = str;
    char *end;
    while (isspace((unsigned char)*start)) start++;
    if (*start == 0) {
        str[0] = '\0';
        return;
    }
    end = start + strlen(start) - 1;
    while (end > start && isspace((unsigned char)*end)) end--;
    end[1] = '\0';
    memmove(str, start, strlen(start) + 1);
}

// 清屏函数
void clearScreen() {
    #ifdef _WIN32
        system("cls");  // Windows 系统
    #else
        system("clear"); // Linux/Mac 系统
    #endif
}

// 将答案字符串标准化(去空格转大写)
void normalizeAnswer(char *dest, const char *src) {
    char temp[MAX_ANSWER_LEN] = {0};
    int idx = 0;
    for (int i = 0; src[i] && idx < MAX_ANSWER_LEN-1; i++) {
        if (!isspace((unsigned char)src[i])) {
            temp[idx++] = toupper(src[i]);
        }
    }
    temp[idx] = '\0';
    strcpy(dest, temp);
}

int main() {
	clearScreen();
    // 设置随机种子(暂未用到,但留用)
    srand(time(NULL));
    loadQuestions();  // 加载题库,若文件不存在则初始化示例题目
    
    int choice;
    do {
        printf("\n========== 中学语文在线考试系统 ==========\n");
        printf("1. 题目管理(管理员)\n");
        printf("2. 参加考试(考生)\n");
        printf("3. 查看历史成绩\n");
        printf("4. 退出系统\n");
        printf("请选择操作: ");
        scanf("%d", &choice);
        getchar(); // 吸收回车
        
        switch (choice) {
            case 1:
                adminMenu();
                break;
            case 2:
                examMenu();
                break;
            case 3:
                showScoreHistory();
                break;
            case 4:
                printf("感谢使用,再见!\n");
                break;
            default:
                printf("无效选择,请重新输入!\n");
        }
    } while (choice != 4);
    
    return 0;
}

// 加载题库(二进制文件)
void loadQuestions() {
    FILE *fp = fopen("questions.dat", "rb");
    if (fp == NULL) {
        // 文件不存在,初始化示例题目(中学语文)
        initSampleQuestions();
        saveQuestions();
        return;
    }
    // 读取题目数量
    if (fread(&g_questionCount, sizeof(int), 1, fp) != 1) {
        fclose(fp);
        initSampleQuestions();
        saveQuestions();
        return;
    }
    if (g_questionCount > MAX_QUESTIONS) g_questionCount = MAX_QUESTIONS;
    // 读取题目数组
    fread(g_questions, sizeof(Question), g_questionCount, fp);
    fclose(fp);
}

// 保存题库到二进制文件
void saveQuestions() {
    FILE *fp = fopen("questions.dat", "wb");
    if (fp == NULL) {
        printf("错误:无法保存题库文件!\n");
        return;
    }
    fwrite(&g_questionCount, sizeof(int), 1, fp);
    fwrite(g_questions, sizeof(Question), g_questionCount, fp);
    fclose(fp);
    printf("题库已保存。\n");
}

// 初始化中学语文示例题目
void initSampleQuestions() {
    g_questionCount = 5;
    // 1. 单选题
    g_questions[0].id = 1;
    g_questions[0].type = TYPE_SINGLE;
    strcpy(g_questions[0].text, "下列哪项不属于《诗经》的艺术特色?");
    strcpy(g_questions[0].options[0], "A. 赋比兴手法");
    strcpy(g_questions[0].options[1], "B. 重章叠句");
    strcpy(g_questions[0].options[2], "C. 以四言为主");
    strcpy(g_questions[0].options[3], "D. 格律严整");
    strcpy(g_questions[0].answer, "D");
    g_questions[0].score = 2.0;
    strcpy(g_questions[0].explanation, "《诗经》以四言为主,多用赋比兴和重章叠句,格律严整是唐代近体诗的特点。");
    
    // 2. 多选题
    g_questions[1].id = 2;
    g_questions[1].type = TYPE_MULTI;
    strcpy(g_questions[1].text, "下列哪些是唐宋八大家中唐代的作家?");
    strcpy(g_questions[1].options[0], "A. 韩愈");
    strcpy(g_questions[1].options[1], "B. 柳宗元");
    strcpy(g_questions[1].options[2], "C. 欧阳修");
    strcpy(g_questions[1].options[3], "D. 王安石");
    strcpy(g_questions[1].answer, "AB");
    g_questions[1].score = 3.0;
    strcpy(g_questions[1].explanation, "唐宋八大家中唐代两位是韩愈、柳宗元,其余六位为宋代。");
    
    // 3. 判断题
    g_questions[2].id = 3;
    g_questions[2].type = TYPE_JUDGE;
    strcpy(g_questions[2].text, "“床前明月光”中的“床”指的是卧具(床铺)。");
    strcpy(g_questions[2].options[0], "A. 正确");
    strcpy(g_questions[2].options[1], "B. 错误");
    strcpy(g_questions[2].answer, "B");
    g_questions[2].score = 1.0;
    strcpy(g_questions[2].explanation, "此处“床”指井栏或坐具,并非卧具。");
    
    // 4. 单选题
    g_questions[3].id = 4;
    g_questions[3].type = TYPE_SINGLE;
    strcpy(g_questions[3].text, "“无边落木萧萧下,不尽长江滚滚来”出自杜甫的哪首诗?");
    strcpy(g_questions[3].options[0], "A. 《春望》");
    strcpy(g_questions[3].options[1], "B. 《登高》");
    strcpy(g_questions[3].options[2], "C. 《望岳》");
    strcpy(g_questions[3].options[3], "D. 《兵车行》");
    strcpy(g_questions[3].answer, "B");
    g_questions[3].score = 2.0;
    strcpy(g_questions[3].explanation, "该句出自杜甫《登高》,被誉为“七律第一”。");
    
    // 5. 判断题
    g_questions[4].id = 5;
    g_questions[4].type = TYPE_JUDGE;
    strcpy(g_questions[4].text, "《史记》是我国第一部纪传体通史。");
    strcpy(g_questions[4].options[0], "A. 正确");
    strcpy(g_questions[4].options[1], "B. 错误");
    strcpy(g_questions[4].answer, "A");
    g_questions[4].score = 1.0;
    strcpy(g_questions[4].explanation, "《史记》由司马迁所著,是中国第一部纪传体通史。");
}

// 管理员菜单
void adminMenu() {
	clearScreen();
    char pwd[20];
    printf("请输入管理员密码(默认admin): ");
    fgets(pwd, sizeof(pwd), stdin);
    trim(pwd);
    if (strcmp(pwd, "admin") != 0) {
        printf("密码错误,返回主菜单。\n");
        return;
    }
    
    int choice;
    do {
        printf("\n===== 题目管理 =====\n");
        printf("1. 添加题目\n");
        printf("2. 修改题目\n");
        printf("3. 删除题目\n");
        printf("4. 查看所有题目\n");
        printf("5. 返回上一级\n");
        printf("请选择: ");
        scanf("%d", &choice);
        getchar();
        
        switch (choice) {
            case 1: addQuestion(); break;
            case 2: editQuestion(); break;
            case 3: deleteQuestion(); break;
            case 4: listQuestions(); break;
            case 5: printf("返回主菜单。\n"); break;
            default: printf("无效选项!\n");
        }
    } while (choice != 5);
}

// 添加题目
void addQuestion() {
    if (g_questionCount >= MAX_QUESTIONS) {
        printf("题库已满,无法添加!\n");
        return;
    }
    Question newQ;
    newQ.id = g_questionCount + 1; // 简单自增
    printf("请输入题目类型(1单选 2多选 3判断): ");
    scanf("%d", &newQ.type);
    getchar();
    if (newQ.type < TYPE_SINGLE || newQ.type > TYPE_JUDGE) {
        printf("无效类型!\n");
        return;
    }
    
    printf("请输入题干: ");
    fgets(newQ.text, MAX_TEXT_LEN, stdin);
    trim(newQ.text);
    
    if (newQ.type != TYPE_JUDGE) {
        for (int i = 0; i < 4; i++) {
            printf("请输入选项%c: ", 'A' + i);
            fgets(newQ.options[i], MAX_OPTION_LEN, stdin);
            trim(newQ.options[i]);
        }
    } else {
        // 判断题固定选项
        strcpy(newQ.options[0], "A. 正确");
        strcpy(newQ.options[1], "B. 错误");
    }
    
    printf("请输入标准答案(单选如A,多选如ABD,判断T/F): ");
    char ansBuf[MAX_ANSWER_LEN];
    fgets(ansBuf, MAX_ANSWER_LEN, stdin);
    normalizeAnswer(newQ.answer, ansBuf);
    
    printf("请输入分值(例如2.0): ");
    scanf("%f", &newQ.score);
    getchar();
    printf("请输入题目解析: ");
    fgets(newQ.explanation, MAX_EXPLANATION_LEN, stdin);
    trim(newQ.explanation);
    
    g_questions[g_questionCount] = newQ;
    g_questionCount++;
    saveQuestions();
    printf("题目添加成功!\n");
}

// 修改题目
void editQuestion() {
    listQuestions();
    int id;
    printf("请输入要修改的题目ID: ");
    scanf("%d", &id);
    getchar();
    int idx = -1;
    for (int i = 0; i < g_questionCount; i++) {
        if (g_questions[i].id == id) {
            idx = i;
            break;
        }
    }
    if (idx == -1) {
        printf("未找到该题目!\n");
        return;
    }
    Question *q = &g_questions[idx];
    printf("当前题目:%s\n", q->text);
    printf("留空表示不修改。\n");
    char buffer[512];
    // 修改题干
    printf("新题干: ");
    fgets(buffer, sizeof(buffer), stdin);
    trim(buffer);
    if (strlen(buffer) > 0) strcpy(q->text, buffer);
    
    // 修改类型(简单起见只提示)
    int newType;
    printf("新类型(1单选2多选3判断,0不变): ");
    scanf("%d", &newType);
    getchar();
    if (newType >= 1 && newType <= 3) q->type = newType;
    
    // 修改选项(非判断题)
    if (q->type != TYPE_JUDGE) {
        for (int i = 0; i < 4; i++) {
            printf("新选项%c: ", 'A'+i);
            fgets(buffer, sizeof(buffer), stdin);
            trim(buffer);
            if (strlen(buffer) > 0) strcpy(q->options[i], buffer);
        }
    }
    // 答案
    printf("新答案: ");
    fgets(buffer, MAX_ANSWER_LEN, stdin);
    trim(buffer);
    if (strlen(buffer) > 0) normalizeAnswer(q->answer, buffer);
    // 分值
    printf("新分值: ");
    fgets(buffer, sizeof(buffer), stdin);
    if (strlen(buffer) > 0) sscanf(buffer, "%f", &q->score);
    // 解析
    printf("新解析: ");
    fgets(buffer, MAX_EXPLANATION_LEN, stdin);
    trim(buffer);
    if (strlen(buffer) > 0) strcpy(q->explanation, buffer);
    
    saveQuestions();
    printf("题目修改成功!\n");
}

// 删除题目
void deleteQuestion() {
    listQuestions();
    int id;
    printf("请输入要删除的题目ID: ");
    scanf("%d", &id);
    getchar();
    int idx = -1;
    for (int i = 0; i < g_questionCount; i++) {
        if (g_questions[i].id == id) {
            idx = i;
            break;
        }
    }
    if (idx == -1) {
        printf("未找到该题目!\n");
        return;
    }
    for (int i = idx; i < g_questionCount - 1; i++) {
        g_questions[i] = g_questions[i+1];
        g_questions[i].id = i+1; // 重新编号
    }
    g_questionCount--;
    saveQuestions();
    printf("题目删除成功!\n");
}

// 列出所有题目
void listQuestions() {
	clearScreen();
    if (g_questionCount == 0) {
        printf("暂无题目,请先添加。\n");
        return;
    }
    printf("\n===== 当前题库列表 =====\n");
    for (int i = 0; i < g_questionCount; i++) {
        Question *q = &g_questions[i];
        printf("ID:%d | 类型:%s | 分值:%.1f\n", q->id,
               q->type == TYPE_SINGLE ? "单选" : (q->type == TYPE_MULTI ? "多选" : "判断"),
               q->score);
        printf("题干: %s\n", q->text);
        if (q->type != TYPE_JUDGE) {
            for (int j = 0; j < 4; j++)
                printf("  %s\n", q->options[j]);
        } else {
            printf("  A.正确  B.错误\n");
        }
        printf("答案: %s\n", q->answer);
        printf("解析: %s\n", q->explanation);
        printf("------------------------\n");
    }
}

// 考生菜单
void examMenu() {
    if (g_questionCount == 0) {
        printf("当前题库为空,请管理员先添加题目。\n");
        return;
    }
    startExam();
}

// 开始考试
void startExam() {
	clearScreen();
    ExamProgress progress;
    int hasProgress = loadProgress(&progress);
    int resume = 0;
    if (hasProgress && progress.totalQuestions == g_questionCount) {
        printf("检测到未完成的考试,是否继续?(y/n): ");
        char ch = getchar();
        getchar();
        if (ch == 'y' || ch == 'Y') {
            resume = 1;
            // 检查是否已超时
            time_t now = time(NULL);
            if (difftime(now, progress.startTime) >= progress.durationSec) {
                printf("考试时间已过,将强制交卷判分!\n");
                calculateAndReport(&progress, "考生");
                deleteProgress();
                return;
            }
        } else {
            deleteProgress();
            resume = 0;
        }
    }
    
    if (!resume) {
        // 新考试
        memset(&progress, 0, sizeof(progress));
        progress.totalQuestions = g_questionCount;
        progress.currentIndex = 0;
        progress.startTime = time(NULL);
        progress.durationSec = EXAM_DURATION_SEC;
        for (int i = 0; i < g_questionCount; i++) {
            progress.answers[i][0] = '\0';
        }
        saveProgress(&progress);
    }
    
    // 开始答题
    char input[20];
    int lastIndex = progress.currentIndex;
    for (int i = lastIndex; i < g_questionCount; i++) {
        progress.currentIndex = i; // 当前正要答的题
        // 超时检测
        time_t now = time(NULL);
        double elapsed = difftime(now, progress.startTime);
        if (elapsed >= progress.durationSec) {
            printf("\n考试时间到!自动交卷。\n");
            break;
        }
        // 显示题目
        Question *q = &g_questions[i];
        printf("\n[第%d题/%d题] 分值:%.1f\n", i+1, g_questionCount, q->score);
        printf("题干: %s\n", q->text);
        if (q->type != TYPE_JUDGE) {
            for (int j = 0; j < 4; j++) {
                printf("%s\n", q->options[j]);
            }
        } else {
            printf("A. 正确   B. 错误\n");
        }
        printf("请输入答案(多选如ABD,交卷输入Q,查看剩余时间输入T): ");
        fgets(input, sizeof(input), stdin);
        trim(input);
        if (strlen(input) == 1 && toupper(input[0]) == 'Q') {
            printf("您已选择提前交卷。\n");
            break;
        }
        if (strlen(input) == 1 && toupper(input[0]) == 'T') {
            int remain = progress.durationSec - (int)elapsed;
            printf("剩余时间: %d分%d秒\n", remain/60, remain%60);
            i--; // 重新回答本题
            continue;
        }
        // 保存答案
        normalizeAnswer(progress.answers[i], input);
        // 实时保存进度
        progress.currentIndex = i+1;
        saveProgress(&progress);
    }
    
    // 判分并生成报告
    calculateAndReport(&progress, "考生");
    deleteProgress(); // 考试完成后删除进度文件
}


// 计算总分
float getFullScore() {
    float total = 0;
    for (int i = 0; i < g_questionCount; i++) {
        total += g_questions[i].score;
    }
    return total;
}

// 自动判分,生成错题报告
void calculateAndReport(ExamProgress *progress, const char *studentName) {
	clearScreen();
    float totalScore = 0;
    int wrongCount = 0;
    int wrongIds[MAX_QUESTIONS];
    char wrongExplanations[MAX_QUESTIONS][MAX_EXPLANATION_LEN+50];
    
    printf("\n========== 考试成绩报告 ==========\n");
    for (int i = 0; i < progress->totalQuestions; i++) {
        Question *q = &g_questions[i];
        char *userAns = progress->answers[i];
        int correct = 0;
        if (strlen(userAns) == 0) {
            correct = 0; // 未作答
        } else {
            correct = compareAnswer(userAns, q->answer, q->type);
        }
        if (correct) {
            totalScore += q->score;
        } else {
            wrongIds[wrongCount] = q->id;
            snprintf(wrongExplanations[wrongCount], MAX_EXPLANATION_LEN+50,
                     "第%d题(%s) 正确答案:%s 您的答案:%s\n解析:%s",
                     q->id, q->type==TYPE_SINGLE?"单选":(q->type==TYPE_MULTI?"多选":"判断"),
                     q->answer, strlen(userAns)?userAns:"未作答", q->explanation);
            wrongCount++;
        }
    }
    
    printf("考生: %s\n", studentName);
    printf("总分: %.1f / %.1f\n", totalScore, getFullScore());
    printf("正确率: %.1f%%\n", totalScore/getFullScore()*100);
    if (wrongCount == 0) {
        printf("恭喜您,全对!\n");
    } else {
        printf("错题数量: %d\n", wrongCount);
        printf("\n===== 错题解析 =====\n");
        for (int i = 0; i < wrongCount; i++) {
            printf("%s\n", wrongExplanations[i]);
            printf("------------------------\n");
        }
    }
    // 保存成绩记录
    saveScoreRecord(studentName, totalScore, getFullScore());
}

// 比较答案是否正确
int compareAnswer(const char *userAns, const char *stdAns, int type) {
    char u[MAX_ANSWER_LEN], s[MAX_ANSWER_LEN];
    strcpy(u, userAns);
    strcpy(s, stdAns);
    // 转为大写比较
    for (int i = 0; u[i]; i++) u[i] = toupper(u[i]);
    for (int i = 0; s[i]; i++) s[i] = toupper(s[i]);
    
    if (type == TYPE_SINGLE || type == TYPE_JUDGE) {
        return strcmp(u, s) == 0;
    } else if (type == TYPE_MULTI) {
        // 多选:比较集合是否相同(排序后比较)
        int lenU = strlen(u);
        int lenS = strlen(s);
        if (lenU != lenS) return 0;
        // 简单排序
        char uSorted[MAX_ANSWER_LEN], sSorted[MAX_ANSWER_LEN];
        strcpy(uSorted, u);
        strcpy(sSorted, s);
        for (int i = 0; i < lenU-1; i++) {
            for (int j = i+1; j < lenU; j++) {
                if (uSorted[i] > uSorted[j]) {
                    char tmp = uSorted[i]; uSorted[i] = uSorted[j]; uSorted[j] = tmp;
                }
                if (sSorted[i] > sSorted[j]) {
                    char tmp = sSorted[i]; sSorted[i] = sSorted[j]; sSorted[j] = tmp;
                }
            }
        }
        return strcmp(uSorted, sSorted) == 0;
    }
    return 0;
}

// 保存考试进度(实时保存答案)
void saveProgress(const ExamProgress *progress) {
    FILE *fp = fopen(PROGRESS_FILE, "wb");
    if (fp == NULL) {
        printf("警告:无法保存考试进度!\n");
        return;
    }
    fwrite(progress, sizeof(ExamProgress), 1, fp);
    fclose(fp);
}

// 加载考试进度,返回值1表示存在有效进度,0表示无
int loadProgress(ExamProgress *progress) {
    FILE *fp = fopen(PROGRESS_FILE, "rb");
    if (fp == NULL) return 0;
    int ret = fread(progress, sizeof(ExamProgress), 1, fp);
    fclose(fp);
    return (ret == 1);
}

// 删除进度文件
void deleteProgress() {
    remove(PROGRESS_FILE);
}

// 保存成绩记录到文件
void saveScoreRecord(const char *name, float score, float fullScore) {
    FILE *fp = fopen(SCORE_FILE, "ab");
    if (fp == NULL) {
        printf("无法保存成绩记录!\n");
        return;
    }
    ScoreRecord rec;
    strncpy(rec.name, name, 49);
    rec.name[49] = '\0';
    rec.examTime = time(NULL);
    rec.totalScore = score;
    rec.fullScore = fullScore;
    fwrite(&rec, sizeof(ScoreRecord), 1, fp);
    fclose(fp);
}

// 显示历史成绩
void showScoreHistory() {
	clearScreen();
    FILE *fp = fopen(SCORE_FILE, "rb");
    if (fp == NULL) {
        printf("暂无历史成绩记录。\n");
        return;
    }
    ScoreRecord rec;
    printf("\n========== 历史成绩 ==========\n");
    int idx = 1;
    while (fread(&rec, sizeof(ScoreRecord), 1, fp) == 1) {
        char timeStr[30];
        struct tm *tm_info = localtime(&rec.examTime);
        strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", tm_info);
        printf("%d. %s  %s  得分: %.1f / %.1f\n", idx++, rec.name, timeStr, rec.totalScore, rec.fullScore);
    }
    fclose(fp);
}

五、算法设计思路

  1. 题型与数据结构设计:
    定义Question结构体统一封装单选、多选、判断三种题型,完整包含题目编号、题型标识、题干内容、选项内容、标准答案、题目分值、题目解析七大核心字段,用单一数据结构适配三类题型,实现数据结构复用与题库数据的统一管理,同时搭配ExamProgress和ScoreRecord两个结构体,分别承载考试答题进度与成绩记录数据,搭建起系统完整的数据存储与流转框架,全程通过全局变量实现模块间数据共享,配合二进制文件完成数据持久化。
  2. 答案标准化与比对算法:
    对用户输入的答案先做去空格+转大写的标准化处理,彻底规避输入空格、大小写不一致导致的误判问题,保障判分公平准确。针对不同题型采用差异化比对逻辑:单选题和判断题直接进行字符串全等匹配,匹配成功即判定为正确;多选题采用字符排序后比对的算法,先将用户答案与标准答案的字符分别排序,再进行字符串匹配,确保ABD、DBA这类顺序不同但选项一致的答案能被判定为正确,大幅提升多选题判分的合理性与严谨性。
  3. 限时考试与超时控制:
    考试启动时实时记录开始时间戳,固定设置10分钟考试时长,考生每作答一道题目前,都会通过当前时间与开始时间的差值计算已用时长,实时校验是否超出规定时限,一旦超过10分钟,系统立即强制触发自动交卷流程,终止答题并启动判分,严格恪守考试限时规则,杜绝超时答题的情况,保证考试流程的规范性。
  4. 进度断点续考算法:
    依托ExamProgress结构体承载考试进度数据,考生答题过程中实时将当前题目序号、已作答的所有题目答案、考试开始时间同步写入专属进度文件,实现答题进度的实时保存。考生重新进入系统时,系统自动读取进度文件,校验题库一致性后,直接恢复上一次的考试状态,精准定位到未完成的题目,实现中途退出后的无缝续考,彻底避免意外退出导致答题数据丢失。
  5. 成绩统计与错题汇总:
    交卷后系统遍历全部题库题目,逐题调用答案比对函数校验对错,同步累加正确题目对应的分值,计算出考生最终总分与考试正确率;与此同时,实时收集所有错题的编号、题型、正确答案、考生作答内容及对应解析,分类汇总错题信息,最终统一生成完整的成绩报告,清晰展示总分、正确率,并逐条呈现错题详情,方便考生复盘纠错。

六、运行结果展示

系统首页
image
管理员登陆
image
添加题目
image
修改题目
image
删除题目
image
查看所有题目
image
考生答题
image
查看考试剩余时间
image
考试成绩报告(含错题解析)
image
查看历史成绩
image


七、结对编程体会

  1. 分工协作提升效率:
    结对过程中,一人负责核心逻辑(结构体设计、文件读写、判分算法),另一人负责交互界面、菜单流程、输入校验与异常处理,模块边界清晰,开发速度明显快于单人开发。
  2. 实时纠错减少 Bug:
    双人共同审查代码逻辑,如答案比对、多选题排序、进度文件读写等关键环节,及时发现数组越界、字符串未截断、文件打开失败未处理等问题,大幅降低调试成本。
  3. 思路互补优化设计:
    针对考试续考、限时交卷、错题展示等功能,通过讨论不断完善交互逻辑,例如增加输入标准化、续考超时判断、成绩格式化显示,让系统更易用、更健壮。
  4. 代码规范与可读性提升:
    结对过程中统一命名规范、函数拆分粒度与注释风格,避免单人开发时代码混乱、函数臃肿的问题,后期维护与功能扩展更便捷。
  5. 沟通与团队意识增强:
    频繁沟通需求细节与实现方案,学会倾听对方思路并达成共识,在功能取舍、逻辑简化上达成一致,提升了团队协作与问题解决能力。
posted @ 2026-04-21 19:08  duo_1  阅读(8)  评论(0)    收藏  举报