|
|
4 months ago | |
|---|---|---|
| .. | ||
| README.md | 4 months ago | |
README.md
中小学数学卷子自动生成程序
一个基于 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类负责调用其他类提供的对外接口,共有登陆和主要操作两个阶段
void Application::run() {
std::cout << "=== 中小学数学试卷自动生成程序 ===" << std::endl;
while(true){
//登录界面
if(!loginPhase()){
break;
}
//主要操作界面
mainOperationPhase();
}
}
2、用户登陆
登陆阶段主要负责接收用户的登录请求,请求成功后初始化一系列必要的资源。
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<PaperManager>(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<std::string> 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提供当前登陆用户的姓名、密码以及年纪信息
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<std::streamsize>::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()生成题目
bool PaperManager::generatePaper(int num){
if(currentGenerator == nullptr)
return false;
questions.clear();
std::string newQuestion;
for(int i=0 ; i<num ; i++){
const int max_Count = 1000;
int current_Count = 0;
do {
current_Count++;
newQuestion = currentGenerator->generateQuestion();
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;
}
小学题目生成
std::string PrimaryGenerator::generateQuestion() {
std::stringstream ss;
const std::vector<std::string> 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() + " = ?";
}
初中题目生成
std::string MiddleGenerator::generateQuestion(){
const int numCount = getRand(1, 5);
std::stringstream ss;
std::vector<std::string> operators = {"+","-","*","/"};
std::vector<std::string> 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() + " = ?";
}
高中题目生成
std::string HighGenerator::generateQuestion(){
const int numCount = getRand(1, 5);
std::stringstream ss;
std::vector<std::string> trigFunctions = {"sin", "cos", "tan"};
std::vector<std::string> operators = {"+","-","*","/"};
std::vector<std::string> 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会遍历历史已经生成和当前正在生成的题目库,避免生成一样的题目
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、题目保存逻辑
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<questions.size() ; i++){
outfile << (i+1) << '.' << questions[i] << "\n\n";
}
return 0;
}
五、核心特性
1、多态题目生成
-
使用抽象基类 QuestionGenerator 定义统一接口
-
具体生成器继承并实现不同难度的题目生成逻辑
-
支持运行时动态切换生成器
2、内存安全
std::unique_ptr 自动管理 PaperManager 生命周期,避免内存泄漏和悬空指针问题