测试1版 #3

Merged
hnu202326010101 merged 1 commits from develop into main 4 months ago

28
.gitignore vendored

@ -1,28 +0,0 @@
# 编译生成的可执行文件
*.exe
*.out
*.o
*.obj
# 临时文件
*.tmp
*.temp
*~
# 系统文件
.DS_Store
Thumbs.db
# IDE文件
.vscode/
.idea/
*.swp
*.swo
# 生成的题目文件夹(用户数据)
xiaoxue*/
chuzhong*/
gaozhong*/
# 日志文件
*.log

@ -1,172 +0,0 @@
# Git版本控制使用说明
## 项目Git结构
根据头歌平台要求,本项目采用以下分支结构:
- **master分支**:稳定版本,用于发布
- **develop分支**:开发版本,用于日常开发
## 快速设置(推荐)
### 方法一:使用自动化脚本
1. 双击项目根目录下的 `git_setup.bat` 文件
2. 按照提示输入Git用户名和邮箱
3. 脚本会自动完成仓库初始化和分支创建
### 方法二:手动设置
如果您熟悉Git命令可以按照以下步骤手动设置
## 详细设置步骤
### 1. 检查Git安装
```bash
git --version
```
如果提示"git不是内部或外部命令"请先安装Git
- 访问https://git-scm.com/download/win
- 下载并安装Git for Windows
### 2. 初始化仓库
```bash
# 进入项目目录
cd "e:\学习通\download\软件项目管理\软件2301班_沈永佳_个人项目"
# 初始化Git仓库
git init
# 配置用户信息
git config user.name "您的姓名"
git config user.email "您的邮箱"
```
### 3. 创建分支结构
```bash
# 创建并切换到develop分支
git checkout -b develop
# 添加所有文件
git add .
# 提交初始版本
git commit -m "初始提交:中小学数学卷子自动生成程序"
# 创建master分支
git checkout -b master
# 合并develop分支到master
git merge develop --no-ff -m "合并develop分支到master稳定版本"
# 切换回develop分支进行日常开发
git checkout develop
```
## 连接头歌平台
### 1. 在头歌平台创建项目
1. 登录头歌平台
2. 创建新项目:`软件2301班_沈永佳_个人项目`
3. 复制项目的Git仓库地址
### 2. 添加远程仓库
```bash
# 添加远程仓库(替换为实际的仓库地址)
git remote add origin https://git.educoder.net/your-username/软件2301班_沈永佳_个人项目.git
# 推送master分支
git push -u origin master
# 推送develop分支
git push -u origin develop
```
## 日常开发流程
### 开发新功能
```bash
# 确保在develop分支
git checkout develop
# 拉取最新代码
git pull origin develop
# 修改代码后提交
git add .
git commit -m "描述您的修改内容"
# 推送到远程develop分支
git push origin develop
```
### 发布稳定版本
```bash
# 切换到master分支
git checkout master
# 合并develop分支的稳定功能
git merge develop --no-ff -m "发布版本v1.x"
# 推送到远程master分支
git push origin master
# 切换回develop分支继续开发
git checkout develop
```
## 常用Git命令
| 命令 | 说明 |
|------|------|
| `git status` | 查看文件状态 |
| `git log --oneline` | 查看提交历史 |
| `git branch` | 查看分支列表 |
| `git checkout [分支名]` | 切换分支 |
| `git add .` | 添加所有修改的文件 |
| `git commit -m "消息"` | 提交修改 |
| `git push origin [分支名]` | 推送到远程分支 |
| `git pull origin [分支名]` | 拉取远程分支 |
## 文件结构说明
```
项目根目录/
├── .git/ # Git版本控制目录
├── .gitignore # Git忽略文件配置
├── src/ # 源代码目录
│ ├── main.cpp # 主程序文件
│ ├── Makefile # 编译配置
│ ├── run.bat # 运行脚本
│ └── run_utf8.bat # UTF-8编码运行脚本
├── doc/ # 文档目录
│ ├── README.md # 项目说明
│ └── Git使用说明.md # 本文档
└── git_setup.bat # Git自动设置脚本
```
## 注意事项
1. **编码问题**确保使用UTF-8编码避免中文乱码
2. **分支管理**
- develop分支用于日常开发
- master分支只用于稳定版本发布
3. **提交信息**:使用清晰的中文描述提交内容
4. **文件忽略**`.gitignore`已配置忽略编译文件和用户数据
## 故障排除
### 问题1Git命令不识别
**解决方案**安装Git for Windows并重启命令行
### 问题2中文文件名乱码
**解决方案**
```bash
git config --global core.quotepath false
```
### 问题3推送失败
**解决方案**:检查网络连接和仓库地址是否正确
## 联系方式
如有问题,请联系:
- 姓名:沈永佳
- 班级软件2301班

@ -12,9 +12,9 @@
- 根据账户类型自动识别出题难度
### 2. 智能题目生成
- **小学题目**2-3个操作数数值范围1-50支持加减
- **初中题目**3-4个操作数数值范围1-100支持加减乘
- **高中题目**4-5个操作数数值范围1-100支持加减乘除
- **小学题目**2-3个操作数数值范围1-50支持加减乘除和括号运算
- **初中题目**3-4个操作数数值范围1-100支持加减乘除和括号运算,并能计算平方和开根号
- **高中题目**4-5个操作数数值范围1-100支持加减乘除和括号运算,并能计算平方和开根号而且题目中至少有一个sin,cos或tan运算符
### 3. 题目管理
- 自动避免同一教师生成重复题目
@ -29,15 +29,15 @@
| 学段 | 用户名 | 密码 |
|------|--------|------|
| 小学 | xiaoxue1 | 123456 |
| 小学 | xiaoxue2 | 123456 |
| 小学 | xiaoxue3 | 123456 |
| 初中 | chuzhong1 | 123456 |
| 初中 | chuzhong2 | 123456 |
| 初中 | chuzhong3 | 123456 |
| 高中 | gaozhong1 | 123456 |
| 高中 | gaozhong2 | 123456 |
| 高中 | gaozhong3 | 123456 |
| 小学 | 张三1 | 123 |
| 小学 | 张三2 | 123 |
| 小学 | 张三3 | 123 |
| 初中 | 李四1 | 123 |
| 初中 | 李四2 | 123 |
| 初中 | 李四3 | 123 |
| 高中 | 王五1 | 123 |
| 高中 | 王五2 | 123 |
| 高中 | 王五3 | 123 |
## 编译和运行
@ -60,24 +60,9 @@ g++ -std=c++17 -Wall -Wextra -O2 -o math_exam_generator main.cpp
```
### 运行程序
#### 方法一使用UTF-8编码脚本推荐解决乱码问题
```bash
cd src
run_utf8.bat
```
#### 方法二:手动设置编码后运行
```bash
cd src
chcp 65001
math_exam_generator.exe
```
#### 方法三:直接运行(可能出现中文乱码)
```bash
cd src
math_exam_generator.exe
./math_exam_generator
```
## 使用说明
@ -85,7 +70,7 @@ math_exam_generator.exe
### 1. 登录
启动程序后,输入用户名和密码,用空格隔开:
```
请输入用户名和密码(用空格隔开): xiaoxue1 123456
请输入用户名和密码(用空格隔开): 张三1 123
```
### 2. 生成试卷
@ -110,13 +95,28 @@ math_exam_generator.exe
```
项目根目录/
├── src/ # 源代码目录
│ ├── main.cpp # 主程序文件
│ └── Makefile # 编译配置文件
├── doc/ # 文档目录
│ └── README.md # 项目说明文档
└── [用户名]/ # 自动生成的用户文件夹
└── 年-月-日-时-分-秒.txt # 生成的试卷文件
├── doc/ # 文档目录
│ ├── README.md # 本项目的详细说明
│ └── README_root.md # 根目录的README
└── src/ # 源代码目录
├── Makefile # 编译配置文件
├── app.cpp # 应用逻辑
├── auth.cpp # 用户认证模块
├── exam.cpp # 试卷生成模块
├── include/ # 头文件目录
├── login.cpp # 登录逻辑
├── main.cpp # 主程序入口
├── math_exam_generator.exe # 编译后的可执行文件
└── utils.cpp # 工具函数
```
*注:程序运行时,会在`src`目录下自动生成`paper`文件夹,用于存放用户生成的试卷。*
```
src/
└── paper/
└── [用户名]/ # 自动生成的用户文件夹
└── 年-月-日-时-分-秒.txt # 生成的试卷文件
```
## 试卷格式示例
@ -127,6 +127,18 @@ math_exam_generator.exe
2. 42 - 15 + 23 = ?
3. 8 + 17 - 9 + 14 = ?
4. 36 * 4 - 20 = ?
5. 16^2 - 4 = ?
6. 2^3 - 1 = ?
7. sin(30°) = ?
8. cos(60°) = ?
9. tan(45°) +2= ?
```
## 技术特点
@ -146,7 +158,7 @@ math_exam_generator.exe
## 版本控制
本项目使用Git进行版本控制
- `master` 分支:稳定版本
- `main` 分支:稳定版本
- `develop` 分支:开发版本
## 作者

@ -1,63 +0,0 @@
@echo off
chcp 65001 >nul
echo ========================================
echo 中小学数学卷子生成程序 Git 设置
echo ========================================
echo.
echo 正在检查Git是否已安装...
git --version >nul 2>&1
if %errorlevel% neq 0 (
echo [错误] 未检测到Git请先安装Git
echo 1. 访问 https://git-scm.com/download/win
echo 2. 下载并安装Git for Windows
echo 3. 重新运行此脚本
pause
exit /b 1
)
echo [成功] Git已安装
echo.
echo 正在初始化Git仓库...
git init
if %errorlevel% neq 0 (
echo [错误] Git初始化失败
pause
exit /b 1
)
echo 正在配置Git用户信息...
set /p username="请输入您的Git用户名: "
set /p email="请输入您的Git邮箱: "
git config user.name "%username%"
git config user.email "%email%"
echo.
echo 正在创建分支结构...
git checkout -b develop
git add .
git commit -m "初始提交:中小学数学卷子自动生成程序"
echo 正在创建master分支...
git checkout -b master
git merge develop --no-ff -m "合并develop分支到master稳定版本"
echo.
echo ========================================
echo Git仓库设置完成
echo.
echo 分支结构:
echo - master: 稳定版本
echo - develop: 开发版本(当前分支)
echo.
echo 下一步:
echo 1. 在头歌平台创建项目仓库
echo 2. 复制仓库地址
echo 3. 运行以下命令添加远程仓库:
echo git remote add origin [仓库地址]
echo git push -u origin master
echo git push -u origin develop
echo ========================================
pause

@ -1,22 +1,28 @@
# Makefile for Math Exam Generator
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -O2
TARGET = math_exam_generator
SOURCE = main.cpp
# Simple Makefile for Math Exam Generator
# Default target
all: $(TARGET)
CXX ?= g++
CXXFLAGS ?= -std=c++17 -Wall -Wextra -O2
# Build target
$(TARGET): $(SOURCE)
$(CXX) $(CXXFLAGS) -o $(TARGET) $(SOURCE)
# Cross-platform remove command
ifeq ($(OS),Windows_NT)
RM := cmd /C del /Q
else
RM := rm -f
endif
# Clean target
clean:
rm -f $(TARGET) $(TARGET).exe
TARGET := math_exam_generator
SRCS := main.cpp app.cpp auth.cpp exam.cpp login.cpp utils.cpp
OBJS := $(SRCS:.cpp=.o)
all: $(TARGET)
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) -o $@ $^
-$(RM) $(OBJS)
# Run target
run: $(TARGET)
./$(TARGET)
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
.PHONY: all clean run
.PHONY: clean
clean:
-$(RM) $(OBJS) $(TARGET) $(TARGET).exe

@ -0,0 +1,92 @@
#include "include/app.hpp"
#include "include/utils.hpp"
#include <iostream>
#include <fstream>
#include <sstream>
namespace fs = std::filesystem;
namespace meg {
App::App() {}
void App::handle_logged_in() {
if (!current_user_) return;
current_level_ = current_user_->level;
while (true) {
std::wcout << L"准备生成" << level_to_chinese(current_level_) << L"数学题目,请输入生成题目数量(输入-1将退出当前用户重新登录";
std::wcout.flush();
std::wstring line;
if (!std::getline(std::wcin, line)) return; // EOF
line = trim(line);
if (line.empty()) continue;
if (line == L"-1") {
std::wcout << L"已退出登录。" << std::endl;
current_user_.reset();
return;
}
// 支持命令:切换为小学/初中/高中
if (starts_with(line, L"切换为")) {
std::wstring t = line.substr(3);
t = trim(t);
if (t == L"小学") current_level_ = Level::Primary;
else if (t == L"初中") current_level_ = Level::Middle;
else if (t == L"高中") current_level_ = Level::High;
else {
std::wcout << L"不支持的类型:" << t << std::endl;
continue;
}
std::wcout << L"已切换为" << level_to_chinese(current_level_) << L"" << std::endl;
continue;
}
// 尝试解析数量
try {
int n = std::stoi(std::string(line.begin(), line.end()));
if (n < 10 || n > 30) {
std::wcout << L"请输入 10-30 之间的数字。" << std::endl;
continue;
}
fs::path base = fs::path("paper") / current_user_->username;
fs::create_directories(base);
auto history_path = base / ".history.txt";
auto history = exam_.load_history(history_path);
auto questions = exam_.generate_unique(current_level_, n, history);
// 保存试卷
std::string ts = now_timestamp_str();
fs::path outfile = base / (ts + ".txt");
std::ofstream fout(outfile);
for (size_t i = 0; i < questions.size(); ++i) {
fout << (i + 1) << ". " << questions[i] << "\n\n";
}
fout.close();
exam_.append_history(history_path, questions);
std::wcout << L"已生成试卷:" << outfile.wstring() << std::endl;
} catch (...) {
std::wcout << L"请输入正确的数字,或使用指令如:切换为初中。" << std::endl;
}
}
}
void App::run() {
init_console_locale();
std::wcout << L"中小学数学卷子自动生成程序" << std::endl;
while (true) {
if (!current_user_) {
auto u = login_.prompt_login();
if (!u) return; // EOF 退出
current_user_ = u;
current_level_ = current_user_->level;
} else {
handle_logged_in();
}
}
}
} // namespace meg

@ -0,0 +1,29 @@
#include "include/auth.hpp"
namespace meg {
Auth::Auth() {
// 小学 张三1..3
users_[L"张三1"] = {L"123", Level::Primary};
users_[L"张三2"] = {L"123", Level::Primary};
users_[L"张三3"] = {L"123", Level::Primary};
// 初中 李四1..3
users_[L"李四1"] = {L"123", Level::Middle};
users_[L"李四2"] = {L"123", Level::Middle};
users_[L"李四3"] = {L"123", Level::Middle};
// 高中 王五1..3
users_[L"王五1"] = {L"123", Level::High};
users_[L"王五2"] = {L"123", Level::High};
users_[L"王五3"] = {L"123", Level::High};
}
std::optional<User> Auth::authenticate(const std::wstring& username, const std::wstring& password) const {
auto it = users_.find(username);
if (it == users_.end()) return std::nullopt;
if (it->second.first != password) return std::nullopt;
return User{username, password, it->second.second};
}
} // namespace meg

@ -0,0 +1,152 @@
#include "include/exam.hpp"
#include <fstream>
#include <iostream>
#include <algorithm>
namespace meg {
static int rand_int(std::mt19937& rng, int lo, int hi) {
std::uniform_int_distribution<int> dist(lo, hi);
return dist(rng);
}
ExamGenerator::ExamGenerator() {
std::random_device rd;
rng_ = std::mt19937(rd());
}
std::unordered_set<std::string> ExamGenerator::load_history(const std::filesystem::path& history_path) {
std::unordered_set<std::string> s;
std::ifstream fin(history_path);
std::string line;
while (std::getline(fin, line)) {
if (!line.empty()) s.insert(line);
}
return s;
}
void ExamGenerator::append_history(const std::filesystem::path& history_path, const std::vector<std::string>& qs) {
std::ofstream fout(history_path, std::ios::app);
for (auto& q : qs) {
fout << q << '\n';
}
}
std::vector<std::string> ExamGenerator::generate_unique(Level level, int count, std::unordered_set<std::string>& history) {
std::vector<std::string> out;
int attempts = 0;
while ((int)out.size() < count && attempts < count * 50) {
std::string q;
if (level == Level::Primary) q = gen_primary();
else if (level == Level::Middle) q = gen_middle();
else q = gen_high();
if (history.insert(q).second) {
out.push_back(q);
}
++attempts;
}
return out;
}
static std::string join_ops(const std::vector<int>& nums, const std::vector<char>& ops) {
std::string s;
for (size_t i = 0; i < nums.size(); ++i) {
s += std::to_string(nums[i]);
if (i < ops.size()) {
s += ' ';
s += ops[i];
s += ' ';
}
}
return s;
}
std::string ExamGenerator::gen_primary() {
int n_ops = rand_int(rng_, 1, 2); // 2-3个操作数 => 1-2个操作符
std::vector<int> nums;
for (int i = 0; i < n_ops + 1; ++i) nums.push_back(rand_int(rng_, 1, 50));
static const char all_ops[] = {'+', '-', '*', '/'};
std::vector<char> ops;
for (int i = 0; i < n_ops; ++i) ops.push_back(all_ops[rand_int(rng_, 0, 3)]);
std::string expr = join_ops(nums, ops);
// 适度加括号
if (nums.size() >= 3 && rand_int(rng_, 0, 1)) {
size_t pos = expr.find(' ');
if (pos != std::string::npos) {
expr = "(" + expr.substr(0, pos) + ")" + expr.substr(pos);
}
}
return expr + " = ?";
}
std::string ExamGenerator::gen_middle() {
int n_ops = rand_int(rng_, 2, 3); // 3-4 操作数
std::vector<int> nums;
for (int i = 0; i < n_ops + 1; ++i) nums.push_back(rand_int(rng_, 1, 100));
static const char all_ops[] = {'+', '-', '*', '/'};
std::vector<char> ops;
for (int i = 0; i < n_ops; ++i) ops.push_back(all_ops[rand_int(rng_, 0, 3)]);
// 随机把一个数变成平方或开方
int idx = rand_int(rng_, 0, (int)nums.size() - 1);
bool use_sqrt = rand_int(rng_, 0, 1) == 1;
std::string expr = join_ops(nums, ops);
// 找到替换位置(简单方式:直接重新拼接)
std::string s;
for (size_t i = 0; i < nums.size(); ++i) {
std::string term = std::to_string(nums[i]);
if ((int)i == idx) {
if (use_sqrt) term = "sqrt(" + term + ")"; // 开根号
else term = term + "^2"; // 平方
}
s += term;
if (i < ops.size()) {
s += ' '; s += ops[i]; s += ' ';
}
}
return s + " = ?";
}
std::string ExamGenerator::gen_high() {
int n_ops = rand_int(rng_, 3, 4); // 4-5 操作数
std::vector<int> nums;
for (int i = 0; i < n_ops + 1; ++i) nums.push_back(rand_int(rng_, 1, 100));
static const char all_ops[] = {'+', '-', '*', '/'};
std::vector<char> ops;
for (int i = 0; i < n_ops; ++i) ops.push_back(all_ops[rand_int(rng_, 0, 3)]);
// 先基础表达式
std::string base;
for (size_t i = 0; i < nums.size(); ++i) {
base += std::to_string(nums[i]);
if (i < ops.size()) { base += ' '; base += ops[i]; base += ' '; }
}
// 至少一个三角函数,角度用 30/45/60/90 之一,使用 "deg" 标识角度
static const char* funcs[] = {"sin", "cos", "tan"};
static const int angles[] = {30, 45, 60, 90};
std::string tri = std::string(funcs[rand_int(rng_, 0, 2)]) + "(" + std::to_string(angles[rand_int(rng_, 0, 3)]) + "deg)";
// 随机把一个数替换为三角项
size_t replace_pos = rand_int(rng_, 0, (int)nums.size() - 1);
// 重建字符串并替换
std::string s;
for (size_t i = 0; i < nums.size(); ++i) {
std::string term = (i == replace_pos) ? tri : std::to_string(nums[i]);
s += term;
if (i < ops.size()) { s += ' '; s += ops[i]; s += ' '; }
}
// 同时可能再加平方或开方
if (rand_int(rng_, 0, 1)) {
s = "(" + s + ")^2";
} else if (rand_int(rng_, 0, 1)) {
s = "sqrt(" + s + ")";
}
return s + " = ?";
}
} // namespace meg

@ -0,0 +1,30 @@
#ifndef MEG_APP_HPP
#define MEG_APP_HPP
#include <optional>
#include <filesystem>
#include "auth.hpp"
#include "login.hpp"
#include "exam.hpp"
namespace meg {
class App {
public:
App();
void run();
private:
Auth auth_;
LoginManager login_{auth_};
ExamGenerator exam_;
std::optional<User> current_user_;
Level current_level_ = Level::Primary;
void handle_logged_in();
};
} // namespace meg
#endif // MEG_APP_HPP

@ -0,0 +1,27 @@
#ifndef MEG_AUTH_HPP
#define MEG_AUTH_HPP
#include <string>
#include <optional>
#include <map>
#include "utils.hpp"
namespace meg {
struct User {
std::wstring username;
std::wstring password;
Level level;
};
class Auth {
public:
Auth();
std::optional<User> authenticate(const std::wstring& username, const std::wstring& password) const;
private:
std::map<std::wstring, std::pair<std::wstring, Level>> users_;
};
} // namespace meg
#endif // MEG_AUTH_HPP

@ -0,0 +1,33 @@
#ifndef MEG_EXAM_HPP
#define MEG_EXAM_HPP
#include <string>
#include <vector>
#include <unordered_set>
#include <filesystem>
#include <random>
#include "utils.hpp"
namespace meg {
class ExamGenerator {
public:
ExamGenerator();
// 加载/保存历史,防止同教师重复题目
std::unordered_set<std::string> load_history(const std::filesystem::path& history_path);
void append_history(const std::filesystem::path& history_path, const std::vector<std::string>& qs);
// 生成不重复题目
std::vector<std::string> generate_unique(Level level, int count, std::unordered_set<std::string>& history);
private:
std::mt19937 rng_;
std::string gen_primary();
std::string gen_middle();
std::string gen_high();
};
} // namespace meg
#endif // MEG_EXAM_HPP

@ -0,0 +1,20 @@
#ifndef MEG_LOGIN_HPP
#define MEG_LOGIN_HPP
#include <optional>
#include <string>
#include "auth.hpp"
namespace meg {
class LoginManager {
public:
explicit LoginManager(const Auth& auth) : auth_(auth) {}
std::optional<User> prompt_login();
private:
const Auth& auth_;
};
} // namespace meg
#endif // MEG_LOGIN_HPP

@ -0,0 +1,25 @@
#ifndef MEG_UTILS_HPP
#define MEG_UTILS_HPP
#include <string>
#include <filesystem>
namespace meg {
enum class Level { Primary, Middle, High };
std::wstring level_to_chinese(Level level);
// 初始化控制台本地化,解决中文输入/输出问题
void init_console_locale();
// 生成时间戳YYYY-MM-DD-HH-MM-SS
std::string now_timestamp_str();
// 字符串工具
bool starts_with(const std::wstring& s, const std::wstring& prefix);
std::wstring trim(const std::wstring& s);
} // namespace meg
#endif // MEG_UTILS_HPP

@ -0,0 +1,38 @@
#include "include/login.hpp"
#include "include/utils.hpp"
#include <iostream>
#include <sstream>
namespace meg {
std::optional<User> LoginManager::prompt_login() {
while (true) {
std::wcout << L"请输入用户名和密码(用空格隔开): ";
std::wcout.flush();
std::wstring line;
if (!std::getline(std::wcin, line)) {
return std::nullopt;
}
line = trim(line);
if (line.empty()) continue;
// 拆分为两段:用户名 密码
std::wistringstream iss(line);
std::wstring u, p;
iss >> u >> p;
if (u.empty() || p.empty()) {
std::wcout << L"输入格式不正确,请重新输入。" << std::endl;
continue;
}
auto user = auth_.authenticate(u, p);
if (user) {
std::wcout << L"登录成功,欢迎您," << u << L"" << std::endl;
return user;
} else {
std::wcout << L"用户名或密码错误,请重试。" << std::endl;
}
}
}
} // namespace meg

@ -1,410 +1,7 @@
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <random>
#include <fstream>
#include <sstream>
#include <ctime>
#include <iomanip>
#include <filesystem>
#include <set>
#include <locale>
#ifdef _WIN32
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#endif
using namespace std;
namespace fs = std::filesystem;
// 用户账号结构
struct User {
string username;
string password;
string type; // "小学", "初中", "高中"
};
// 数学题目结构
struct MathQuestion {
string question;
int answer;
bool operator<(const MathQuestion& other) const {
return question < other.question;
}
};
class MathExamGenerator {
private:
vector<User> users;
User currentUser;
bool isLoggedIn;
string currentType;
random_device rd;
mt19937 gen;
// 存储已生成的题目,避免重复
map<string, set<MathQuestion>> generatedQuestions;
public:
MathExamGenerator() : gen(rd()), isLoggedIn(false) {
initializeUsers();
loadExistingQuestions();
}
// 初始化预设账号
void initializeUsers() {
// 小学账号
users.push_back({"xiaoxue1", "123456", "小学"});
users.push_back({"xiaoxue2", "123456", "小学"});
users.push_back({"xiaoxue3", "123456", "小学"});
// 初中账号
users.push_back({"chuzhong1", "123456", "初中"});
users.push_back({"chuzhong2", "123456", "初中"});
users.push_back({"chuzhong3", "123456", "初中"});
// 高中账号
users.push_back({"gaozhong1", "123456", "高中"});
users.push_back({"gaozhong2", "123456", "高中"});
users.push_back({"gaozhong3", "123456", "高中"});
}
// 加载已存在的题目
void loadExistingQuestions() {
for (const auto& user : users) {
string folderPath = user.username;
if (fs::exists(folderPath)) {
for (const auto& entry : fs::directory_iterator(folderPath)) {
if (entry.is_regular_file() && entry.path().extension() == ".txt") {
loadQuestionsFromFile(entry.path().string(), user.username);
}
}
}
}
}
// 从文件加载题目
void loadQuestionsFromFile(const string& filename, const string& username) {
ifstream file(filename);
string line;
while (getline(file, line)) {
if (!line.empty() && line.find('.') != string::npos) {
// 提取题目部分(去掉题号)
size_t dotPos = line.find('.');
if (dotPos != string::npos && dotPos + 1 < line.length()) {
string question = line.substr(dotPos + 1);
// 去掉前后空格
question.erase(0, question.find_first_not_of(" \t"));
question.erase(question.find_last_not_of(" \t") + 1);
MathQuestion mq;
mq.question = question;
mq.answer = 0; // 这里简化处理
generatedQuestions[username].insert(mq);
}
}
}
file.close();
}
// 用户登录
bool login(const string& username, const string& password) {
for (const auto& user : users) {
if (user.username == username && user.password == password) {
currentUser = user;
currentType = user.type;
isLoggedIn = true;
cout << "当前选择为" << currentType << "出题" << endl;
return true;
}
}
return false;
}
// 生成小学数学题
MathQuestion generatePrimaryQuestion() {
uniform_int_distribution<> opCount(2, 3); // 操作数2-3个
uniform_int_distribution<> numDist(1, 50); // 数值范围1-50
uniform_int_distribution<> opDist(0, 1); // 0:加法, 1:减法
int operandCount = opCount(gen);
vector<int> numbers;
vector<char> operators;
for (int i = 0; i < operandCount; i++) {
numbers.push_back(numDist(gen));
}
for (int i = 0; i < operandCount - 1; i++) {
operators.push_back(opDist(gen) == 0 ? '+' : '-');
}
// 构建题目字符串
stringstream ss;
ss << numbers[0];
int result = numbers[0];
for (int i = 0; i < operators.size(); i++) {
ss << " " << operators[i] << " " << numbers[i + 1];
if (operators[i] == '+') {
result += numbers[i + 1];
} else {
result -= numbers[i + 1];
}
}
// 确保结果为正数
if (result < 0) {
return generatePrimaryQuestion();
}
ss << " = ?";
MathQuestion question;
question.question = ss.str();
question.answer = result;
return question;
}
// 生成初中数学题
MathQuestion generateMiddleQuestion() {
uniform_int_distribution<> opCount(3, 4); // 操作数3-4个
uniform_int_distribution<> numDist(1, 100); // 数值范围1-100
uniform_int_distribution<> opDist(0, 2); // 0:加法, 1:减法, 2:乘法
int operandCount = opCount(gen);
vector<int> numbers;
vector<char> operators;
for (int i = 0; i < operandCount; i++) {
numbers.push_back(numDist(gen));
}
for (int i = 0; i < operandCount - 1; i++) {
int op = opDist(gen);
if (op == 0) operators.push_back('+');
else if (op == 1) operators.push_back('-');
else operators.push_back('*');
}
// 构建题目字符串
stringstream ss;
ss << numbers[0];
int result = numbers[0];
for (int i = 0; i < operators.size(); i++) {
ss << " " << operators[i] << " " << numbers[i + 1];
if (operators[i] == '+') {
result += numbers[i + 1];
} else if (operators[i] == '-') {
result -= numbers[i + 1];
} else {
result *= numbers[i + 1];
}
}
ss << " = ?";
MathQuestion question;
question.question = ss.str();
question.answer = result;
return question;
}
// 生成高中数学题
MathQuestion generateHighQuestion() {
uniform_int_distribution<> opCount(4, 5); // 操作数4-5个
uniform_int_distribution<> numDist(1, 100); // 数值范围1-100
uniform_int_distribution<> opDist(0, 3); // 0:加法, 1:减法, 2:乘法, 3:除法
int operandCount = opCount(gen);
vector<int> numbers;
vector<char> operators;
for (int i = 0; i < operandCount; i++) {
numbers.push_back(numDist(gen));
}
for (int i = 0; i < operandCount - 1; i++) {
int op = opDist(gen);
if (op == 0) operators.push_back('+');
else if (op == 1) operators.push_back('-');
else if (op == 2) operators.push_back('*');
else {
operators.push_back('/');
// 确保除法结果为整数
numbers[i + 1] = numbers[i] % numbers[i + 1] == 0 ? numbers[i + 1] :
(numbers[i] / numbers[i + 1] + 1);
}
}
// 构建题目字符串
stringstream ss;
ss << numbers[0];
double result = numbers[0];
for (int i = 0; i < operators.size(); i++) {
ss << " " << operators[i] << " " << numbers[i + 1];
if (operators[i] == '+') {
result += numbers[i + 1];
} else if (operators[i] == '-') {
result -= numbers[i + 1];
} else if (operators[i] == '*') {
result *= numbers[i + 1];
} else {
if (numbers[i + 1] != 0) {
result /= numbers[i + 1];
}
}
}
ss << " = ?";
MathQuestion question;
question.question = ss.str();
question.answer = (int)result;
return question;
}
// 根据类型生成题目
MathQuestion generateQuestion(const string& type) {
if (type == "小学") {
return generatePrimaryQuestion();
} else if (type == "初中") {
return generateMiddleQuestion();
} else {
return generateHighQuestion();
}
}
// 检查题目是否重复
bool isQuestionDuplicate(const MathQuestion& question) {
return generatedQuestions[currentUser.username].count(question) > 0;
}
// 生成试卷
void generateExam(int questionCount) {
vector<MathQuestion> questions;
// 生成不重复的题目
while (questions.size() < questionCount) {
MathQuestion question = generateQuestion(currentType);
if (!isQuestionDuplicate(question)) {
questions.push_back(question);
generatedQuestions[currentUser.username].insert(question);
}
}
// 创建用户文件夹
string folderPath = currentUser.username;
if (!fs::exists(folderPath)) {
fs::create_directory(folderPath);
}
// 生成文件名(年-月-日-时-分-秒.txt
auto now = chrono::system_clock::now();
auto time_t = chrono::system_clock::to_time_t(now);
auto tm = *localtime(&time_t);
stringstream filename;
filename << folderPath << "/"
<< put_time(&tm, "%Y-%m-%d-%H-%M-%S") << ".txt";
// 写入文件
ofstream file(filename.str());
for (int i = 0; i < questions.size(); i++) {
file << (i + 1) << ". " << questions[i].question << endl;
if (i < questions.size() - 1) {
file << endl; // 题目之间空一行
}
}
file.close();
cout << "试卷已生成并保存到: " << filename.str() << endl;
}
// 切换类型
bool switchType(const string& newType) {
if (newType == "小学" || newType == "初中" || newType == "高中") {
currentType = newType;
return true;
}
return false;
}
// 主运行循环
void run() {
cout << "=== 中小学数学卷子自动生成程序 ===" << endl;
while (true) {
if (!isLoggedIn) {
cout << "请输入用户名和密码(用空格隔开): ";
string username, password;
cin >> username >> password;
if (login(username, password)) {
continue;
} else {
cout << "请输入正确的用户名、密码" << endl;
continue;
}
}
cout << "准备生成" << currentType << "数学题目,请输入生成题目数量(输入-1将退出当前用户重新登录";
string input;
cin >> input;
// 检查是否是切换命令
if (input.find("切换为") == 0) {
string newType = input.substr(3); // 去掉"切换为"
if (switchType(newType)) {
cout << "准备生成" << currentType << "数学题目,请输入生成题目数量:";
continue;
} else {
cout << "请输入小学、初中和高中三个选项中的一个" << endl;
continue;
}
}
try {
int count = stoi(input);
if (count == -1) {
isLoggedIn = false;
cout << "已退出登录" << endl;
continue;
}
if (count >= 10 && count <= 30) {
generateExam(count);
} else {
cout << "题目数量必须在10-30之间" << endl;
}
} catch (const exception& e) {
cout << "请输入有效的数字" << endl;
}
}
}
};
int main() {
#ifdef _WIN32
// 设置控制台编码为UTF-8解决中文乱码问题
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
// 设置locale为中文
setlocale(LC_ALL, "zh_CN.UTF-8");
#endif
MathExamGenerator generator;
generator.run();
return 0;
#include "include/app.hpp"
int main() {
meg::App app;
app.run();
return 0;
}

Binary file not shown.

@ -0,0 +1,30 @@
40 + 31 * 38 = ?
46 * 6 = ?
(8) - 32 / 31 = ?
36 + 20 + 17 = ?
(7) + 14 / 39 = ?
(19) - 24 * 20 = ?
(13) - 27 * 43 = ?
43 / 17 - 39 = ?
17 - 43 = ?
9 / 30 = ?
48 / 31 = ?
17 - 24 = ?
(3) - 23 * 30 = ?
26 / 38 = ?
(48) / 46 * 33 = ?
11 * 7 = ?
(47) / 17 * 31 = ?
36 * 10 / 14 = ?
47 * 23 / 32 = ?
(29) - 14 * 33 = ?
33 * 46 + 38 = ?
(4) * 11 * 9 = ?
(39) / 36 - 36 = ?
25 - 4 = ?
12 - 8 = ?
39 - 32 = ?
13 / 25 = ?
40 - 43 = ?
(48) + 44 + 28 = ?
(12) / 9 + 44 = ?

@ -0,0 +1,60 @@
1. 40 + 31 * 38 = ?
2. 46 * 6 = ?
3. (8) - 32 / 31 = ?
4. 36 + 20 + 17 = ?
5. (7) + 14 / 39 = ?
6. (19) - 24 * 20 = ?
7. (13) - 27 * 43 = ?
8. 43 / 17 - 39 = ?
9. 17 - 43 = ?
10. 9 / 30 = ?
11. 48 / 31 = ?
12. 17 - 24 = ?
13. (3) - 23 * 30 = ?
14. 26 / 38 = ?
15. (48) / 46 * 33 = ?
16. 11 * 7 = ?
17. (47) / 17 * 31 = ?
18. 36 * 10 / 14 = ?
19. 47 * 23 / 32 = ?
20. (29) - 14 * 33 = ?
21. 33 * 46 + 38 = ?
22. (4) * 11 * 9 = ?
23. (39) / 36 - 36 = ?
24. 25 - 4 = ?
25. 12 - 8 = ?
26. 39 - 32 = ?
27. 13 / 25 = ?
28. 40 - 43 = ?
29. (48) + 44 + 28 = ?
30. (12) / 9 + 44 = ?

@ -1,11 +0,0 @@
@echo off
echo 正在编译程序...
g++ -std=c++17 -Wall -Wextra -O2 -o math_exam_generator.exe main.cpp
if %errorlevel% equ 0 (
echo 编译成功!正在启动程序...
echo.
math_exam_generator.exe
) else (
echo 编译失败!
pause
)

@ -1,12 +0,0 @@
@echo off
chcp 65001 >nul
echo 正在编译程序...
g++ -std=c++17 -Wall -Wextra -O2 -o math_exam_generator.exe main.cpp
if %errorlevel% equ 0 (
echo 编译成功!正在启动程序...
echo.
math_exam_generator.exe
) else (
echo 编译失败!
pause
)

@ -0,0 +1,74 @@
#include "include/utils.hpp"
#include <locale>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <chrono>
#ifdef _WIN32
# include <io.h>
# include <fcntl.h>
#endif
namespace meg {
std::wstring level_to_chinese(Level level) {
switch (level) {
case Level::Primary: return L"小学";
case Level::Middle: return L"初中";
case Level::High: return L"高中";
}
return L"小学";
}
void init_console_locale() {
#ifdef _WIN32
_setmode(_fileno(stdout), _O_U16TEXT);
_setmode(_fileno(stdin), _O_U16TEXT);
_setmode(_fileno(stderr), _O_U16TEXT);
#else
try {
std::locale loc("");
std::locale::global(loc);
std::wcout.imbue(loc);
std::wcin.imbue(loc);
std::wcerr.imbue(loc);
} catch (...) {
// 忽略本地化设置失败
}
#endif
}
std::string now_timestamp_str() {
using namespace std::chrono;
auto now = system_clock::now();
std::time_t t = system_clock::to_time_t(now);
std::tm tm{};
#ifdef _WIN32
localtime_s(&tm, &t);
#else
localtime_r(&t, &tm);
#endif
std::ostringstream oss;
oss << std::put_time(&tm, "%Y-%m-%d-%H-%M-%S");
return oss.str();
}
bool starts_with(const std::wstring& s, const std::wstring& prefix) {
if (prefix.size() > s.size()) return false;
return std::equal(prefix.begin(), prefix.end(), s.begin());
}
static inline bool is_space(wchar_t c) {
return c == L' ' || c == L'\t' || c == L'\n' || c == L'\r' || c == L'\f' || c == L'\v';
}
std::wstring trim(const std::wstring& s) {
size_t b = 0, e = s.size();
while (b < e && is_space(s[b])) ++b;
while (e > b && is_space(s[e-1])) --e;
return s.substr(b, e - b);
}
} // namespace meg
Loading…
Cancel
Save