最终版本

main
FLW 4 months ago
parent fd58f1afdc
commit e9dbffa0d1

@ -0,0 +1,3 @@
{
"CurrentProjectSetting": null
}

@ -0,0 +1,6 @@
{
"ExpandedNodes": [
""
],
"PreviewInSolutionExplorer": false
}

Binary file not shown.

@ -0,0 +1,41 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\27887\\Desktop\\exam\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\27887\\Desktop\\exam\\main.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:main.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 1,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:0:0:{34e76e81-ee4a-11d0-ae2e-00a0c90fffc3}"
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "main.cpp",
"DocumentMoniker": "C:\\Users\\27887\\Desktop\\exam\\main.cpp",
"RelativeDocumentMoniker": "main.cpp",
"ToolTip": "C:\\Users\\27887\\Desktop\\exam\\main.cpp",
"RelativeToolTip": "main.cpp",
"ViewState": "AgIAAAAAAAAAAAAAAADwvwAAAAATAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000677|",
"WhenOpened": "2025-09-24T15:15:56.502Z",
"EditorCaption": ""
}
]
}
]
}
]
}

Binary file not shown.

@ -0,0 +1,18 @@
{
"configurations": [
{
"name": "windows-gcc-x64",
"includePath": [
"${workspaceFolder}/**"
],
"compilerPath": "D:/mingw64/bin/gcc.exe",
"cStandard": "${default}",
"cppStandard": "${default}",
"intelliSenseMode": "windows-gcc-x64",
"compilerArgs": [
""
]
}
],
"version": 4
}

@ -0,0 +1,24 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "C/C++ Runner: Debug Session",
"type": "cppdbg",
"request": "launch",
"args": [],
"stopAtEntry": false,
"externalConsole": true,
"cwd": "c:/Users/27887/Desktop/exam",
"program": "c:/Users/27887/Desktop/exam/build/Debug/outDebug",
"MIMode": "gdb",
"miDebuggerPath": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}

@ -0,0 +1,59 @@
{
"C_Cpp_Runner.cCompilerPath": "gcc",
"C_Cpp_Runner.cppCompilerPath": "g++",
"C_Cpp_Runner.debuggerPath": "gdb",
"C_Cpp_Runner.cStandard": "",
"C_Cpp_Runner.cppStandard": "",
"C_Cpp_Runner.msvcBatchPath": "C:/Program Files/Microsoft Visual Studio/VR_NR/Community/VC/Auxiliary/Build/vcvarsall.bat",
"C_Cpp_Runner.useMsvc": false,
"C_Cpp_Runner.warnings": [
"-Wall",
"-Wextra",
"-Wpedantic",
"-Wshadow",
"-Wformat=2",
"-Wcast-align",
"-Wconversion",
"-Wsign-conversion",
"-Wnull-dereference"
],
"C_Cpp_Runner.msvcWarnings": [
"/W4",
"/permissive-",
"/w14242",
"/w14287",
"/w14296",
"/w14311",
"/w14826",
"/w44062",
"/w44242",
"/w14905",
"/w14906",
"/w14263",
"/w44265",
"/w14928"
],
"C_Cpp_Runner.enableWarnings": true,
"C_Cpp_Runner.warningsAsError": false,
"C_Cpp_Runner.compilerArgs": [],
"C_Cpp_Runner.linkerArgs": [],
"C_Cpp_Runner.includePaths": [],
"C_Cpp_Runner.includeSearch": [
"*",
"**/*"
],
"C_Cpp_Runner.excludeSearch": [
"**/build",
"**/build/**",
"**/.*",
"**/.*/**",
"**/.vscode",
"**/.vscode/**"
],
"C_Cpp_Runner.useAddressSanitizer": false,
"C_Cpp_Runner.useUndefinedSanitizer": false,
"C_Cpp_Runner.useLeakSanitizer": false,
"C_Cpp_Runner.showCompilationTime": false,
"C_Cpp_Runner.useLinkTimeOptimization": false,
"C_Cpp_Runner.msvcSecureNoWarnings": false
}

28
.vscode/tasks.json vendored

@ -0,0 +1,28 @@
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: g++.exe 生成活动文件",
"command": "D:\\vscode\\mingw64\\bin\\g++.exe",
"args": [
"-fdiagnostics-color=always",
"-g",
"${file}",
"-o",
"${fileDirname}\\${fileBasenameNoExtension}.exe"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "调试器生成的任务。"
}
],
"version": "2.0.0"
}

@ -0,0 +1,11 @@
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -I.
LDFLAGS = -pthread
SOURCES = src/auth_manager.cpp src/question_generator.cpp src/primary_generator.cpp src/junior_generator.cpp src/senior_generator.cpp src/file_manager.cpp src/session_manager.cpp src/main.cpp
TARGET = math_exam_generator
$(TARGET): $(SOURCES)
$(CXX) $(CXXFLAGS) $(SOURCES) $(LDFLAGS) -o $(TARGET)
clean:
rm -f $(TARGET)

@ -0,0 +1,43 @@
#include "auth_manager.h"
#include <string>
#include <utility>
#include <unordered_map>
AuthManager::AuthManager() {
// 初始化预设的教师账户信息,包括小学、初中、高中各三个账号
accounts_ = {
{"张三1", {"123", "primary"}},
{"张三2", {"123", "primary"}},
{"张三3", {"123", "primary"}},
{"李四1", {"123", "junior"}},
{"李四2", {"123", "junior"}},
{"李四3", {"123", "junior"}},
{"王五1", {"123", "senior"}},
{"王五2", {"123", "senior"}},
{"王五3", {"123", "senior"}}
};
}
bool AuthManager::Authenticate(const std::string& username,
const std::string& password,
std::string* user_type) {
// 验证输入不包含非法字符
if (username.find(' ') != std::string::npos ||
password.find(' ') != std::string::npos) {
return false;
}
auto it = accounts_.find(username);
if (it != accounts_.end() && it->second.first == password) {
*user_type = it->second.second;
return true;
}
return false;
}
bool AuthManager::IsValidUser(const std::string& username,
const std::string& password) {
std::string user_type;
return Authenticate(username, password, &user_type);
}

@ -0,0 +1,23 @@
#ifndef AUTH_MANAGER_H_
#define AUTH_MANAGER_H_
#include <string>
#include <unordered_map>
class AuthManager {
public:
AuthManager();
// 用户认证返回认证结果并通过user_type输出用户类型
bool Authenticate(const std::string& username, const std::string& password,
std::string* user_type);
// 验证用户是否存在且密码正确
bool IsValidUser(const std::string& username, const std::string& password);
private:
// 存储账户信息:用户名 -> (密码, 用户类型)
std::unordered_map<std::string, std::pair<std::string, std::string>> accounts_;
};
#endif // AUTH_MANAGER_H_

@ -0,0 +1,175 @@
#include "file_manager.h"
#include <iostream>
#include <fstream>
#include <ctime>
#include <string>
#include <vector>
#include <direct.h> // Windows目录操作
#include <windows.h> // Windows API
#include <unordered_map>
std::string FileManager::UsernameToPinyin(const std::string& username) {
// 用户名到拼音的映射
static const std::unordered_map<std::string, std::string> name_map = {
{"张三1", "zhangsan1"},
{"张三2", "zhangsan2"},
{"张三3", "zhangsan3"},
{"李四1", "lisi1"},
{"李四2", "lisi2"},
{"李四3", "lisi3"},
{"王五1", "wangwu1"},
{"王五2", "wangwu2"},
{"王五3", "wangwu3"}
};
auto it = name_map.find(username);
if (it != name_map.end()) {
return it->second;
}
// 如果没有找到映射,返回原用户名
return username;
}
bool FileManager::CreateUserDirectory(const std::string& username) {
std::string pinyin_username = UsernameToPinyin(username);
const std::string dir_path = std::string(kBasePath) + pinyin_username;
// 先创建基础目录(如果不存在)
_mkdir(kBasePath);
// 使用Windows API创建用户目录
if (CreateDirectoryA(dir_path.c_str(), NULL)) {
return true;
} else {
// 如果目录已存在ERROR_ALREADY_EXISTS是正常的
return GetLastError() == ERROR_ALREADY_EXISTS;
}
}
std::string FileManager::GenerateFilename() {
std::time_t now = std::time(nullptr);
std::tm local_time;
localtime_s(&local_time, &now); // 使用安全的版本
char buffer[80];
std::strftime(buffer, sizeof(buffer), "%Y%m%d_%H%M%S", &local_time);
return std::string(buffer) + ".txt";
}
bool FileManager::SaveQuestions(const std::string& username,
const std::vector<std::string>& questions) {
if (questions.empty()) {
std::cerr << "错误:问题列表为空" << std::endl;
return false;
}
// 确保用户目录存在
if (!CreateUserDirectory(username)) {
std::cerr << "错误:无法创建用户目录: " << username << std::endl;
return false;
}
std::string pinyin_username = UsernameToPinyin(username);
const std::string dir_path = std::string(kBasePath) + pinyin_username;
const std::string filename = GenerateFilename();
const std::string full_path = dir_path + "\\" + filename;
std::ofstream file(full_path);
if (!file.is_open()) {
std::cerr << "错误:无法创建文件: " << full_path << std::endl;
return false;
}
// 写入文件头信息
file << "用户名: " << username << " (" << pinyin_username << ")\n";
file << "生成时间: " << filename.substr(0, filename.length() - 4) << "\n";
file << "题目数量: " << questions.size() << "\n";
file << "==============================\n\n";
// 写入题目
for (size_t i = 0; i < questions.size(); ++i) {
file << "" << (i + 1) << "题: " << questions[i] << "\n\n";
}
file.close();
std::cout << "✓ 题目已保存至: " << full_path << std::endl;
return true;
}
bool FileManager::IsQuestionExists(const std::string& username,
const std::string& question) {
const auto existing_questions = LoadExistingQuestions(username);
// 简单的字符串匹配(可改进为模糊匹配)
for (const auto& existing : existing_questions) {
if (existing.find(question) != std::string::npos ||
question.find(existing) != std::string::npos) {
return true;
}
}
return false;
}
std::vector<std::string> FileManager::LoadExistingQuestions(const std::string& username) {
std::vector<std::string> questions;
std::string pinyin_username = UsernameToPinyin(username);
const std::string dir_path = std::string(kBasePath) + pinyin_username;
const std::string search_path = dir_path + "\\*.txt";
// 检查目录是否存在
DWORD attrib = GetFileAttributesA(dir_path.c_str());
if (attrib == INVALID_FILE_ATTRIBUTES || !(attrib & FILE_ATTRIBUTE_DIRECTORY)) {
return questions; // 目录不存在
}
WIN32_FIND_DATAA find_file_data;
HANDLE h_find = FindFirstFileA(search_path.c_str(), &find_file_data);
if (h_find == INVALID_HANDLE_VALUE) {
return questions; // 没有找到文件
}
do {
if (!(find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
const std::string filename = find_file_data.cFileName;
const std::string file_path = dir_path + "\\" + filename;
std::ifstream file(file_path);
if (file.is_open()) {
std::string line;
bool in_questions_section = false;
while (std::getline(file, line)) {
// 跳过文件头,直到分隔线
if (line.find("==============================") != std::string::npos) {
in_questions_section = true;
continue;
}
if (in_questions_section && !line.empty()) {
// 匹配 "第X题: " 格式
if (line.find("") == 0 && line.find("题: ") != std::string::npos) {
size_t pos = line.find("题: ");
if (pos != std::string::npos) {
std::string question_text = line.substr(pos + 3); // "题: " 长度是3
// 移除Windows换行符
if (!question_text.empty() && question_text.back() == '\r') {
question_text.pop_back();
}
if (!question_text.empty()) {
questions.push_back(question_text);
}
}
}
}
}
file.close();
}
}
} while (FindNextFileA(h_find, &find_file_data) != 0);
FindClose(h_find);
return questions;
}

@ -0,0 +1,28 @@
#ifndef FILE_MANAGER_H_
#define FILE_MANAGER_H_
#include <string>
#include <vector>
class FileManager {
public:
FileManager() = default;
bool CreateUserDirectory(const std::string& username);
std::string GenerateFilename();
bool SaveQuestions(const std::string& username,
const std::vector<std::string>& questions);
bool IsQuestionExists(const std::string& username,
const std::string& question);
std::vector<std::string> LoadExistingQuestions(const std::string& username);
private:
static constexpr const char* kBasePath = "exams\\"; // Windows·<73><C2B7><EFBFBD>ָ<EFBFBD><D6B8><EFBFBD>
// <20><><EFBFBD>ÿ<EFBFBD><C3BF><EFBFBD>
FileManager(const FileManager&) = delete;
FileManager& operator=(const FileManager&) = delete;
std::string UsernameToPinyin(const std::string& username);
};
#endif // FILE_MANAGER_H_

@ -0,0 +1,76 @@
#include "junior_generator.h"
#include <sstream>
#include <string>
#include <vector>
std::vector<std::string> JuniorGenerator::GenerateQuestions(int count,
const std::string& username) {
current_user_ = username;
std::vector<std::string> questions;
questions.reserve(count);
for (int i = 0; i < count; ++i) {
questions.push_back(GenerateSingleQuestion());
}
return questions;
}
std::string JuniorGenerator::GenerateSingleQuestion() {
static const std::vector<char> kOperators = {'+', '-', '*', '/'};
const int operand_count = GetRandomNumber(kMinOperands, kMaxOperands);
std::stringstream ss;
bool has_special_operator = false;
// 第一个操作数可以是普通数字或特殊运算符
if (operand_count == 1 || GetRandomNumber(0, 1)) {
// 单操作数情况或第一个操作数是特殊运算符
if (GetRandomNumber(0, 1)) {
ss << "sqrt(" << GetRandomNumber(kMinNumber, kMaxNumber) << ")";
} else {
ss << GetRandomNumber(1, 10) << "^2";
}
has_special_operator = true;
} else {
// 第一个操作数是普通数字
ss << GetRandomNumber(kMinNumber, kMaxNumber);
}
// 添加后续操作数
for (int i = 1; i < operand_count; ++i) {
const char op = GetRandomOperator(kOperators);
// 随机决定是添加普通数字还是特殊运算符
if (!has_special_operator || GetRandomNumber(0, 1)) {
if (GetRandomNumber(0, 1)) {
ss << " " << op << " sqrt(" << GetRandomNumber(kMinNumber, kMaxNumber) << ")";
} else {
ss << " " << op << " " << GetRandomNumber(1, 10) << "^2";
}
has_special_operator = true;
} else {
ss << " " << op << " " << GetRandomNumber(kMinNumber, kMaxNumber);
}
}
// 确保至少有一个特殊运算符
if (!has_special_operator) {
const char op = GetRandomOperator(kOperators);
if (GetRandomNumber(0, 1)) {
ss << " " << op << " sqrt(" << GetRandomNumber(kMinNumber, kMaxNumber) << ")";
} else {
ss << " " << op << " " << GetRandomNumber(1, 10) << "^2";
}
}
ss << " = ?";
return ss.str();
}
bool JuniorGenerator::MeetsDifficultyRequirements(const std::string& question) {
// 使用ASCII字符而不是特殊Unicode字符
return question.find("^2") != std::string::npos ||
question.find("sqrt") != std::string::npos;
}

@ -0,0 +1,21 @@
#ifndef JUNIOR_GENERATOR_H_
#define JUNIOR_GENERATOR_H_
#include "question_generator.h"
class JuniorGenerator : public QuestionGenerator {
public:
std::vector<std::string> GenerateQuestions(int count,
const std::string& username) override;
bool MeetsDifficultyRequirements(const std::string& question) override;
private:
static constexpr int kMinOperands = 1;
static constexpr int kMaxOperands = 5;
static constexpr int kMinNumber = 1;
static constexpr int kMaxNumber = 100;
std::string GenerateSingleQuestion();
};
#endif // JUNIOR_GENERATOR_H_

@ -0,0 +1,156 @@
#include <iostream>
#include <string>
#include <sstream>
#include <cstdlib>
#include "auth_manager.h"
#include "session_manager.h"
#include "file_manager.h"
// 显示欢迎界面
void ShowWelcome() {
std::cout << "=====================================" << std::endl;
std::cout << " 中小学数学卷子自动生成系统" << std::endl;
std::cout << "=====================================" << std::endl;
}
// 显示主菜单
void ShowMainMenu(const std::string& username, const std::string& difficulty) {
std::cout << "\n=====================================" << std::endl;
std::cout << "当前用户: " << username << " (" << difficulty << "老师)" << std::endl;
std::cout << "当前出题类型: " << difficulty << std::endl;
std::cout << "=====================================" << std::endl;
std::cout << "请选择操作:" << std::endl;
std::cout << "1. 生成题目(输入题目数量 10-30" << std::endl;
std::cout << "2. 切换难度(输入:切换为小学/初中/高中)" << std::endl;
std::cout << "3. 退出登录(输入:-1" << std::endl;
std::cout << "> ";
}
int main() {
AuthManager auth_manager;
SessionManager session_manager;
FileManager file_manager;
while (true) {
ShowWelcome();
std::cout << "请输入用户名和密码(格式:用户名 密码),或输入 quit 退出系统:" << std::endl; // 修改提示
std::cout << "> ";
std::string input;
std::getline(std::cin, input);
// 去除前后空格
input.erase(0, input.find_first_not_of(" "));
input.erase(input.find_last_not_of(" ") + 1);
// 检查是否要退出系统(只在首页)
if (input == "quit" || input == "exit" || input == "退出") {
std::cout << "感谢使用中小学数学卷子自动生成系统,再见!" << std::endl;
break; // 退出整个程序
}
std::istringstream iss(input);
std::string username, password;
iss >> username >> password;
// 检查输入格式
if (username.empty() || password.empty()) {
std::cout << "请输入正确的用户名、密码" << std::endl;
continue;
}
// 检查是否正好有两个token
std::vector<std::string> tokens;
std::string token;
while (iss >> token) {
tokens.push_back(token);
}
if (!tokens.empty()) { // 如果还有更多token说明输入格式错误
std::cout << "输入格式错误:请输入用户名和密码,用空格分隔" << std::endl;
continue;
}
std::string user_type;
if (auth_manager.Authenticate(username, password, &user_type)) {
const char* difficulty_name =
(user_type == "primary") ? "小学" :
(user_type == "junior") ? "初中" : "高中";
std::cout << "登录成功!当前选择为" << difficulty_name << "出题" << std::endl;
session_manager.SetUser(username, user_type);
file_manager.CreateUserDirectory(username);
// 主循环
bool logged_in = true;
while (logged_in) {
ShowMainMenu(username, session_manager.GetCurrentDifficulty());
std::string command;
std::getline(std::cin, command);
// 去除前后空格
command.erase(0, command.find_first_not_of(" "));
command.erase(command.find_last_not_of(" ") + 1);
if (command.empty()) {
continue;
}
// 首先检查退出命令
if (command == "-1") {
std::cout << "退出当前用户,重新登录..." << std::endl;
logged_in = false;
continue;
}
// 检查切换难度命令
if (command.find("切换为") == 0) {
std::string new_difficulty = command.substr(9);
if (new_difficulty == "小学" || new_difficulty == "初中" || new_difficulty == "高中") {
std::string difficulty_type =
(new_difficulty == "小学") ? "primary" :
(new_difficulty == "初中") ? "junior" : "senior";
if (session_manager.SwitchDifficulty(difficulty_type)) {
std::cout << "切换成功!当前出题类型: " << new_difficulty << std::endl;
}
} else {
std::cout << "请输入小学、初中和高中三个选项中的一个" << std::endl;
}
continue;
}
// 尝试解析为数字(题目数量)
try {
int count = std::stoi(command);
if (count >= 10 && count <= 30) {
std::cout << "正在生成" << session_manager.GetCurrentDifficulty()
<< "数学题目,数量: " << count << "..." << std::endl;
QuestionGenerator* generator = session_manager.GetCurrentGenerator();
auto questions = generator->GenerateQuestions(count, username);
if (file_manager.SaveQuestions(username, questions)) {
std::cout << "题目生成完成!已保存到文件。" << std::endl;
} else {
std::cout << "文件保存失败!" << std::endl;
}
} else {
std::cout << "题目数量范围应为10-30请重新输入" << std::endl;
}
} catch (const std::exception& e) {
std::cout << "无效命令,请重新输入" << std::endl;
std::cout << "有效命令10-30生成题目、-1退出登录、切换为小学/初中/高中" << std::endl;
}
}
} else {
std::cout << "请输入正确的用户名、密码" << std::endl;
}
}
return 0;
}

@ -0,0 +1,25 @@
用户名: 李四1 (lisi1)
生成时间: 20250928_181147
题目数量: 10
==============================
第1题: sqrt(93) / sqrt(46) * 73 - 32 = ?
第2题: 75 * 9^2 / sqrt(88) / 35 + 2^2 = ?
第3题: 60 / sqrt(98) + 41 = ?
第4题: sqrt(31) = ?
第5题: 8^2 = ?
第6题: 94 + sqrt(84) / 16 = ?
第7题: 18 * 4^2 / 70 - 61 / sqrt(15) = ?
第8题: sqrt(40) / 1^2 - sqrt(34) / 3^2 = ?
第9题: 1^2 / 86 / 10^2 + 10^2 / 64 = ?
第10题: sqrt(1) + sqrt(56) / sqrt(66) / 9 = ?

@ -0,0 +1,25 @@
用户名: 张三1 (zhangsan1)
生成时间: 20250928_181110
题目数量: 10
==============================
第1题: 79 + 88 * 67 - 90 = ?
第2题: 89 - 97 = ?
第3题: 64 / 50 - 85 + 51 = ?
第4题: 81 + 40 = ?
第5题: 98 + 98 - 17 = ?
第6题: 10 * 27 * 16 / 12 = ?
第7题: 94 / 28 + 73 + 84 * 80 = ?
第8题: 64 / 35 + 51 = ?
第9题: 62 * 49 = ?
第10题: 46 * 63 / 13 * 75 = ?

Binary file not shown.

@ -0,0 +1,42 @@
#include "primary_generator.h"
#include <sstream>
#include <string>
#include <vector>
std::vector<std::string> PrimaryGenerator::GenerateQuestions(int count,
const std::string& username) {
current_user_ = username;
std::vector<std::string> questions;
questions.reserve(count); // 预分配空间提高效率
for (int i = 0; i < count; ++i) {
questions.push_back(GenerateSingleQuestion());
}
return questions;
}
std::string PrimaryGenerator::GenerateSingleQuestion() {
static const std::vector<char> kOperators = {'+', '-', '*', '/'};
const int operand_count = GetRandomNumber(kMinOperands, kMaxOperands);
std::stringstream ss;
ss << GetRandomNumber(kMinNumber, kMaxNumber); // 第一个操作数
for (int i = 1; i < operand_count; ++i) {
const char op = GetRandomOperator(kOperators);
ss << " " << op << " " << GetRandomNumber(kMinNumber, kMaxNumber);
}
ss << " = ?";
return ss.str();
}
bool PrimaryGenerator::MeetsDifficultyRequirements(const std::string& question) {
// 小学题目只需要包含基本运算符:+ - * /
return question.find('+') != std::string::npos ||
question.find('-') != std::string::npos ||
question.find('*') != std::string::npos ||
question.find('/') != std::string::npos;
}

@ -0,0 +1,21 @@
#ifndef PRIMARY_GENERATOR_H_
#define PRIMARY_GENERATOR_H_
#include "question_generator.h"
class PrimaryGenerator : public QuestionGenerator {
public:
std::vector<std::string> GenerateQuestions(int count,
const std::string& username) override;
bool MeetsDifficultyRequirements(const std::string& question) override;
private:
static constexpr int kMinOperands = 2;
static constexpr int kMaxOperands = 5;
static constexpr int kMinNumber = 1;
static constexpr int kMaxNumber = 100;
std::string GenerateSingleQuestion();
};
#endif // PRIMARY_GENERATOR_H_

@ -0,0 +1,19 @@
#include "question_generator.h"
#include <chrono>
#include <random>
QuestionGenerator::QuestionGenerator() {
rng_.seed(std::chrono::steady_clock::now().time_since_epoch().count());
}
int QuestionGenerator::GetRandomNumber(int min, int max) {
std::uniform_int_distribution<int> dist(min, max);
return dist(rng_);
}
char QuestionGenerator::GetRandomOperator(const std::vector<char>& operators) {
if (operators.empty()) return '+';
std::uniform_int_distribution<int> dist(0, operators.size() - 1);
return operators[dist(rng_)];
}

@ -0,0 +1,37 @@
#ifndef QUESTION_GENERATOR_H_
#define QUESTION_GENERATOR_H_
#include <string>
#include <vector>
#include <random>
class QuestionGenerator {
public:
virtual ~QuestionGenerator() = default;
// 生成指定数量的数学题目
virtual std::vector<std::string> GenerateQuestions(int count,
const std::string& username) = 0;
// 检查题目是否符合难度要求
virtual bool MeetsDifficultyRequirements(const std::string& question) = 0;
protected:
QuestionGenerator();
// 生成指定范围内的随机数
int GetRandomNumber(int min, int max);
// 从运算符列表中随机选择一个运算符
char GetRandomOperator(const std::vector<char>& operators);
std::string current_user_;
std::mt19937 rng_;
private:
// 禁用拷贝构造和赋值操作
QuestionGenerator(const QuestionGenerator&) = delete;
QuestionGenerator& operator=(const QuestionGenerator&) = delete;
};
#endif // QUESTION_GENERATOR_H_

@ -0,0 +1,203 @@
g++ -Wall -Wextra -g3 -std=c++17 -pthread *.cpp -o output/main.exe
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
中小学数学卷子自动生成系统
? 项目简介
中小学数学卷子自动生成系统是一个基于C++开发的命令行应用程序旨在帮助小学、初中和高中数学老师快速生成符合教学大纲要求的数学题目。系统支持题目查重、难度切换、文件保存等功能严格遵循Google C++代码规范。
? 功能特性
? 用户认证系统
预设小学、初中、高中各三个教师账户
安全的用户名密码验证机制
自动识别用户类型并设置相应难度
? 题目生成能力
小学题目包含加减乘除基本运算操作数2-5个
初中题目:必须包含平方(?)或开根号(√)运算符,支持单操作数
高中题目:必须包含三角函数(sin/cos/tan),支持单操作数
智能查重:避免生成重复题目
? 交互功能
动态难度切换(小学/初中/高中)
题目数量控制10-30题
实时题目预览
用户友好的命令行界面
? 文件管理
按用户分文件夹存储
时间戳命名的文件格式(年-月-日-时-分-秒.txt
题目编号和格式化输出
?? 技术架构
类设计(面向对象)
QuestionGenerator抽象基类
├── PrimaryGenerator小学题目生成器
├── JuniorGenerator初中题目生成器
└── SeniorGenerator高中题目生成器
核心管理类:
├── AuthManager认证管理
├── SessionManager会话管理
├── FileManager文件管理
设计模式
工厂模式:题目生成器的多态实现
单一职责原则:每个类专注特定功能
开放封闭原则:易于扩展新难度级别
? 安装与运行
环境要求
操作系统Windows 10/11 或 Linux (WSL)
编译器GCC 8.1.0+ 支持C++17
构建工具直接使用g++
# 直接编译
g++ -Wall -Wextra -g3 -std=c++17 -pthread *.cpp -o output/main.exe
# 运行程序
chcp 65001
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
./main.exe
??? 使用指南
登录系统
=====================================
中小学数学卷子自动生成系统
=====================================
请输入用户名和密码(格式:用户名 密码),或输入 quit 退出系统:
> 张三1 123
登录成功!当前选择为小学出题
=====================================
当前用户: 张三1 (小学老师)
当前出题类型: 小学
=====================================
请选择操作:
1. 生成题目(输入题目数量 10-30
2. 切换难度(输入:切换为小学/初中/高中)
3. 退出登录(输入:-1
> 15
测试账户
用户类型 用户名 密码 默认难度
小学 张三1, 张三2, 张三3 123 小学
初中 李四1, 李四2, 李四3 123 初中
高中 王五1, 王五2, 王五3 123 高中
? 题目示例
小学题目
1. 3 + 5 × 2 = ?
2. 24 ÷ 3 + 7 - 2 = ?
初中题目
1. √(16) + 5 = ?
2. 4? × 3 - 2 = ?
3. √(25) = ? (单操作数)
高中题目
1. sin(30°) × 2 = ?
2. 5 + cos(45°) - 1 = ?
3. tan(60°) = ? (单操作数)
? 项目结构
exam/
├── src/
│ ├── main.cpp # 程序入口
│ ├── auth_manager.h/cpp # 认证管理
│ ├── question_generator.h/cpp # 题目生成基类
│ ├── primary_generator.h/cpp # 小学题目生成
│ ├── junior_generator.h/cpp # 初中题目生成
│ ├── senior_generator.h/cpp # 高中题目生成
│ ├── file_manager.h/cpp # 文件管理
│ └── session_manager.h/cpp # 会话管理
├── exams/ # 生成的题目文件
│ ├── zhangsan1/
│ ├── lisi1/
│ └── wangwu1/
├── Makefile # 编译配置
└── README.md # 项目说明
? 开发指南
代码规范
遵循Google C++代码规范
使用大驼峰命名法(类名)
成员变量以下划线结尾
包含详细的注释文档
扩展新功能
添加新难度级别继承QuestionGenerator基类
修改题目规则:调整各生成器的算法参数
增强文件格式修改FileManager的输出格式
编译选项
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -I.
LDFLAGS = -pthread
? 故障排除
常见问题
编译错误确保GCC支持C++17检查文件路径
文件权限:确保有创建目录和文件的权限
编码问题系统使用UTF-8编码确保终端支持中文显示
调试模式
? 未来规划
功能增强
图形用户界面Qt版本
题目难度分级(简单/中等/困难)
答案生成和批改功能
题目导出为PDF格式
网络协作和题目共享
技术优化
单元测试覆盖
性能优化和内存管理
跨平台兼容性增强
配置文件和个性化设置
开发流程
Fork本项目
创建功能分支git checkout -b feature/AmazingFeature
提交更改git commit -m 'Add some AmazingFeature'
推送到分支git push origin feature/AmazingFeature
开启Pull Request
? 致谢
感谢所有为这个项目提供建议和帮助的贡献者!
开发者软件2303班 郭永瑞
最后更新2025年9月
版本v1.0.0

@ -0,0 +1,72 @@
#include "senior_generator.h"
#include <sstream>
#include <string>
#include <vector>
std::vector<std::string> SeniorGenerator::GenerateQuestions(int count,
const std::string& username) {
current_user_ = username;
std::vector<std::string> questions;
questions.reserve(count);
for (int i = 0; i < count; ++i) {
questions.push_back(GenerateSingleQuestion());
}
return questions;
}
std::string SeniorGenerator::GenerateSingleQuestion() {
static const std::vector<char> kOperators = {'+', '-', '*', '/'};
static const std::vector<std::string> kTrigFunctions = {"sin", "cos", "tan"};
const int operand_count = GetRandomNumber(kMinOperands, kMaxOperands);
std::stringstream ss;
bool has_trig_function = false;
// 第一个操作数可以是普通数字或三角函数
if (operand_count == 1 || GetRandomNumber(0, 1)) {
// 单操作数情况或第一个操作数是三角函数
const int trig_index = GetRandomNumber(0, kTrigFunctions.size() - 1);
ss << kTrigFunctions[trig_index] << "("
<< GetRandomNumber(kMinNumber, kMaxAngle) << "°)";
has_trig_function = true;
} else {
// 第一个操作数是普通数字
ss << GetRandomNumber(kMinNumber, kMaxNumber);
}
// 添加后续操作数
for (int i = 1; i < operand_count; ++i) {
const char op = GetRandomOperator(kOperators);
// 随机决定是添加普通数字还是三角函数
if (!has_trig_function || GetRandomNumber(0, 1)) {
const int trig_index = GetRandomNumber(0, kTrigFunctions.size() - 1);
ss << " " << op << " " << kTrigFunctions[trig_index] << "("
<< GetRandomNumber(kMinNumber, kMaxAngle) << "°)";
has_trig_function = true;
} else {
ss << " " << op << " " << GetRandomNumber(kMinNumber, kMaxNumber);
}
}
// 确保至少有一个三角函数
if (!has_trig_function) {
const char op = GetRandomOperator(kOperators);
const int trig_index = GetRandomNumber(0, kTrigFunctions.size() - 1);
ss << " " << op << " " << kTrigFunctions[trig_index] << "("
<< GetRandomNumber(kMinNumber, kMaxAngle) << "°)";
}
ss << " = ?";
return ss.str();
}
bool SeniorGenerator::MeetsDifficultyRequirements(const std::string& question) {
// 高中题目至少包含一个三角函数
return question.find("sin") != std::string::npos ||
question.find("cos") != std::string::npos ||
question.find("tan") != std::string::npos;
}

@ -0,0 +1,22 @@
#ifndef SENIOR_GENERATOR_H_
#define SENIOR_GENERATOR_H_
#include "question_generator.h"
class SeniorGenerator : public QuestionGenerator {
public:
std::vector<std::string> GenerateQuestions(int count,
const std::string& username) override;
bool MeetsDifficultyRequirements(const std::string& question) override;
private:
static constexpr int kMinOperands = 1;
static constexpr int kMaxOperands = 5;
static constexpr int kMinNumber = 1;
static constexpr int kMaxNumber = 100;
static constexpr int kMaxAngle = 90;
std::string GenerateSingleQuestion();
};
#endif // SENIOR_GENERATOR_H_

@ -0,0 +1,40 @@
#include "session_manager.h"
SessionManager::SessionManager()
: current_user_(""),
current_difficulty_(""),
current_generator_(nullptr) {}
void SessionManager::SetUser(const std::string& username,
const std::string& difficulty) {
current_user_ = username;
SwitchDifficulty(difficulty);
}
bool SessionManager::SwitchDifficulty(const std::string& difficulty) {
if (difficulty == "primary") {
current_generator_ = &primary_generator_;
current_difficulty_ = "小学";
} else if (difficulty == "junior") {
current_generator_ = &junior_generator_;
current_difficulty_ = "初中";
} else if (difficulty == "senior") {
current_generator_ = &senior_generator_;
current_difficulty_ = "高中";
} else {
return false;
}
return true;
}
QuestionGenerator* SessionManager::GetCurrentGenerator() {
return current_generator_;
}
std::string SessionManager::GetCurrentDifficulty() const {
return current_difficulty_;
}
std::string SessionManager::GetCurrentUser() const {
return current_user_;
}

@ -0,0 +1,43 @@
#ifndef SESSION_MANAGER_H_
#define SESSION_MANAGER_H_
#include <string>
#include "question_generator.h"
#include "primary_generator.h"
#include "junior_generator.h"
#include "senior_generator.h"
class SessionManager {
public:
SessionManager();
// 设置当前用户和难度
void SetUser(const std::string& username, const std::string& difficulty);
// 切换难度级别
bool SwitchDifficulty(const std::string& difficulty);
// 获取当前题目生成器
QuestionGenerator* GetCurrentGenerator();
// 获取当前难度描述
std::string GetCurrentDifficulty() const;
// 获取当前用户名
std::string GetCurrentUser() const;
private:
std::string current_user_;
std::string current_difficulty_;
QuestionGenerator* current_generator_;
PrimaryGenerator primary_generator_;
JuniorGenerator junior_generator_;
SeniorGenerator senior_generator_;
// 禁用拷贝构造和赋值操作
SessionManager(const SessionManager&) = delete;
SessionManager& operator=(const SessionManager&) = delete;
};
#endif // SESSION_MANAGER_H_
Loading…
Cancel
Save