# 中小学数学卷子自动生成程序 一个基于 C++ 的命令行应用程序,为小学、初中和高中教师自动生成数学试卷,确保题目唯一性。 ## 一、项目概述 本项目采用面向对象设计,通过多级难度题目生成器和智能查重机制,为不同学段的数学教师提供便捷的试卷生成工具。 ## 二、编译运行 ### 环境要求 - C++17 或更高版本 - 支持标准库的 C++ 编译器(g++、clang++、MSVC) ### 运行 - **Windows操作系统在PowerShell中执行:chcp 65001; .\main.exe** - **Windows操作系统在CMD中执行:chcp 65001 && main.exe** - Windows采用本地语言编码(GBK),而程序用UTF-8输出,从而导致中文乱码。chcp 65001将设置Windows当前终端会话用UTF-8编码来显示文字,以此来解决中文乱码问题。 ## 三、核心类设计 ### 类关系图 Application ├── User (用户信息) ├── PaperManager (试卷管理) └── QuestionGenerator (题目生成器接口) ├── PrimaryGenerator (小学) ├── MiddleGenerator (初中) └── HighGenerator (高中) ### 类详细说明 #### Application - 应用程序主控制器 **职责**:程序入口,用户交互流程控制,协调各个模块协作 **关键方法**: - `run()` - 程序主循环 - `loginPhase()` - 处理用户登录 - `mainOperationPhase()` - 主操作逻辑 - `createGenerator(Grade)` - 工厂方法创建题目生成器 - `switchGenerator(string)` - 动态切换生成器 #### User - 用户信息管理 **职责**:存储和管理用户身份信息及权限等级 **属性**: - `username` - 用户名 - `password` - 密码 - `grade` - 学段等级(Primary/Middle/High) **关键方法**: - `userInital()` - 用户信息初始化 - `getGradString()` - 获取学段字符串表示 #### QuestionGenerator - 题目生成器抽象基类 **职责**:定义题目生成的统一接口,提供随机数工具 **设计特点**: - 使用现代 C++ 随机数库 (`random_device`, `mt19937`) - 纯虚函数 `generateQuestion()` 强制子类实现 - 模板方法模式提供 `getRand()` 工具函数 #### 具体生成器类 - **PrimaryGenerator** - 小学数学题目生成 - **MiddleGenerator** - 初中数学题目生成 - **HighGenerator** - 高中数学题目生成 #### PaperManager - 试卷管理器 **职责**:试卷生成、查重、文件保存的全生命周期管理 **属性**: - `questions` - 当前已生成试卷 - `historys` - 历史试卷 **关键方法**: - `void setGenerator(QuestionGenerator* newGenerator);` -生成题目 - `bool generatePaper(int num);` - 生成试卷 - `int loadHistoryQuestions();` - 加载用户历史试卷 - `bool isDulicate(const std::string& question);` - 查重逻辑 - `int savePaperToFile();` - 保存试卷 ## 四、主要代码 ### 1、程序入口 Application类负责调用其他类提供的对外接口,共有登陆和主要操作两个阶段 ```cpp void Application::run() { std::cout << "=== 中小学数学试卷自动生成程序 ===" << std::endl; while(true){ //登录界面 if(!loginPhase()){ break; } //主要操作界面 mainOperationPhase(); } } ``` ### 2、用户登陆 登陆阶段主要负责接收用户的登录请求,请求成功后初始化一系列必要的资源。 ```cpp bool Application::loginPhase(){ std::cout << "0. 退出系统" << std::endl; std::cout << "1. 登录" << std::endl; int logresult; std::string choice; while(true){ std::cout << "在此处输入您的选择:"; std::getline(std::cin, choice); if(choice == "0"){ return false; } else if(choice == "1"){ logresult = handlelogin(); } else{ std::cout << "请根据提示输入正确的选项!!!" << std::endl; continue; } if(logresult == 0){ QuestionGenerator* questiongenerator = createGenerator(myUser.getGrad()); myPaperManager = std::make_unique(myUser.getUsername(), questiongenerator); myPaperManager->loadHistoryQuestions(); return true; } else if(logresult == -1){ std::cout << "文件连接出错,请等待技术人员修复..." << std::endl; system("pause"); } else std::cout << "账号或者密码错误!" << std::endl; } } int Application::handlelogin() { std::string str, username, password; std::vector tokens, account; account = split(getLoginInput()); if(account.size() == 2){ username = account[0]; password = account[1]; } else{ return 1; } std::ifstream infile("users.txt"); if (!infile) { std::cerr << "Unable to open file users.txt"; return -1; } while (getline(infile, str)) { tokens = split(str, ' '); if (tokens.size() == 3 && tokens[1] == username && tokens[2] == password) { std::string level = tokens[0]; Grade grade; if (level == "小学") { grade = Primary; } else if (level == "初中") { grade = Middle; } else if (level == "高中") { grade = High; } else { grade = Unknown; } myUser.userInital(username, password, grade); return 0; } } infile.close(); return 1; } ``` ### 3、主要操作 这一阶段是主要是通过调用Application类的私有成员myPaperManager和myUser来完成的 myPaperManager会根据用户的年级以及需求调用对应的Generator来生成问题 myUser提供当前登陆用户的姓名、密码以及年纪信息 ```cpp bool Application::mainOperationPhase(){ std::cout << "登录成功!当前选择为" << myUser.getGradString() << "出题" << std::endl; std::cout << "选项:" << std::endl; std::cout << "1. 输入10-30的数字:生成对应数量的题目" << std::endl; std::cout << "2. 输入'切换为小学/初中/高中':切换出题难度" << std::endl; std::cout << "3. 输入-1:退出当前用户,重新登陆" << std::endl; while (true) { std::cout << "请输入指令: "; std::string str ; std::cin >> str; std::cin.ignore(std::numeric_limits::max(), '\n'); if(str.find("切换为") == 0){ switchGenerator(str.substr(9)); } else if(isInteger(str)) { int num = std::stoi(str); if(num >= 10 && num <= 30){ if(myPaperManager->generatePaper(num)) std::cout << "试卷生成成功!" << std::endl; } else if(num == -1){ myPaperManager.reset(); return false; } else { std::cout << "请输入正确的数字!!!" << std::endl; } } else { std::cout << "输入无效,请重新输入" << std::endl; } } } ``` ### 4、试卷生成逻辑 myPaperManager的成员变量currentGenerator调用generateQuestion()生成题目 ```cpp bool PaperManager::generatePaper(int num){ if(currentGenerator == nullptr) return false; questions.clear(); std::string newQuestion; for(int i=0 ; igenerateQuestion(); if(current_Count >= max_Count) break; } while(isDulicate(newQuestion)); questions.push_back(newQuestion); } if((savePaperToFile() == 0)){ for(const auto& question : questions){ historys.insert(question); } return true; } return false; } ``` #### 小学题目生成 ```cpp std::string PrimaryGenerator::generateQuestion() { std::stringstream ss; const std::vector operators = {"+", "-", "*", "/"}; const int numCount = getRand(2, 5); const bool useParentheses = (getRand(0, 1) == 1) && (numCount > 2); if (useParentheses) { int leftPos = getRand(0, numCount - 2); int rightPos = getRand(leftPos + 1, numCount - 1); for (int i = 0; i < numCount; i++) { if (i == leftPos) { ss << "("; } ss << getRand(1, 100); if (i == rightPos) { ss << ")"; } if (i < numCount - 1) { ss << " " << operators[getRand(0, 3)] << " "; } } } else { for (int i = 0; i < numCount; i++) { ss << getRand(1, 100); if (i < numCount - 1) { ss << " " << operators[getRand(0, 3)] << " "; } } } return ss.str() + " = ?"; } ``` #### 初中题目生成 ```cpp std::string MiddleGenerator::generateQuestion(){ const int numCount = getRand(1, 5); std::stringstream ss; std::vector operators = {"+","-","*","/"}; std::vector operands; bool hasAdvanced = false; for (int i = 0; i < numCount; i++) { int num = getRand(1, 100); std::string operand = std::to_string(num); if (numCount == 1 || getRand(0, 1) == 1) { hasAdvanced = true; if (getRand(0, 1) == 0) { operand = operand + "²"; } else { operand = "√" + operand; } } operands.push_back(operand); } if (!hasAdvanced) { if (getRand(0, 1) == 0) { operands[0] = operands[0] + "²"; } else { operands[0] = "√" + operands[0]; } hasAdvanced = true; } for (int i = 0; i < numCount; i++) { ss << operands[i]; if (i < numCount - 1) { ss << " " << operators[getRand(0,3)] << " "; } } return ss.str() + " = ?"; } ``` #### 高中题目生成 ```cpp std::string HighGenerator::generateQuestion(){ const int numCount = getRand(1, 5); std::stringstream ss; std::vector trigFunctions = {"sin", "cos", "tan"}; std::vector operators = {"+","-","*","/"}; std::vector operands; bool hasTrig = false; for (int i = 0; i < numCount; i++) { int num = getRand(1, 100); std::string operand = std::to_string(num); if (numCount == 1 || getRand(0, 1) == 1) { hasTrig = true; std::string trigFunc = trigFunctions[getRand(0,2)]; operand = trigFunc + operand + "°"; } operands.push_back(operand); } if (!hasTrig) { operands[0] = trigFunctions[getRand(0,2)] + operands[0] + "°"; hasTrig = true; } for (int i = 0; i < numCount; i++) { ss << operands[i]; if (i < numCount - 1) { ss << " " << operators[getRand(0,3)] << " "; } } return ss.str() + " = ?"; } ``` ### 5、题目查重逻辑 对当前生成的问题,isDulicate会遍历历史已经生成和当前正在生成的题目库,避免生成一样的题目 ```cpp bool PaperManager::isDulicate(const std::string& question){ if(historys.find(question) != historys.end()) return true; if(std::find(questions.begin(), questions.end() ,question) != questions.end()) return true; return false; } ``` ### 6、题目保存逻辑 ```cpp int PaperManager::savePaperToFile(){ std::string str = simpleChineseToPinyin(username); std::string floderPath = "../paper/" + str + "/"; std::filesystem::create_directories(floderPath) ; std::string filename = floderPath + getCurrntTimeString() + ".txt"; std::ofstream outfile(filename); if (!outfile) { std::cerr << "Unable to open file " << filename << std::endl; return -1; } for(size_t i = 0 ; i