final #5

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

@ -16,6 +16,7 @@
- **初中题目**3-4个操作数数值范围1-100支持加减乘除和括号运算,并能计算平方和开根号
- **高中题目**4-5个操作数数值范围1-100支持加减乘除和括号运算,并能计算平方和开根号而且题目中至少有一个sin,cos或tan运算符
### 3. 题目管理
- 自动避免同一教师生成重复题目
- 按教师账号创建独立文件夹
@ -56,7 +57,7 @@ make
#### 手动编译
```bash
cd src
g++ -std=c++17 -Wall -Wextra -O2 -o math_exam_generator main.cpp
g++ -std=c++17 -Wall -Wextra -O2 -o math_exam_generator main.cc app.cc auth.cc exam.cc login.cc utils.cc
```
### 运行程序
@ -100,14 +101,14 @@ cd src
│ └── README_root.md # 根目录的README
└── src/ # 源代码目录
├── Makefile # 编译配置文件
├── app.cpp # 应用逻辑
├── auth.cpp # 用户认证模块
├── exam.cpp # 试卷生成模块
├── app.cc # 应用逻辑
├── auth.cc # 用户认证模块
├── exam.cc # 试卷生成模块
├── include/ # 头文件目录
├── login.cpp # 登录逻辑
├── main.cpp # 主程序入口
├── login.cc # 登录逻辑
├── main.cc # 主程序入口
├── math_exam_generator.exe # 编译后的可执行文件
└── utils.cpp # 工具函数
└── utils.cc # 工具函数
```
*注:程序运行时,会在`src`目录下自动生成`paper`文件夹,用于存放用户生成的试卷。*
@ -144,10 +145,12 @@ src/
## 技术特点
- **面向对象设计**:使用类封装功能模块
- **STL容器**:使用 vector、map、set 等容器管理数据
- **文件系统操作**:使用 C++17 filesystem 库进行文件管理
- **随机数生成**使用现代C++随机数生成器
- **异常处理**:对用户输入进行验证和异常处理
- **STL 容器**:使用 vector、map、set 等容器管理数据
- **文件系统操作**:使用 C++17 `<filesystem>` 进行文件管理
- **随机数生成**:使用现代 C++ 随机数生成器
- **显式输入校验**:对用户输入进行验证,避免使用异常进行控制流
- **统一命名与常量**:函数采用 UpperCamelCase常量统一 `k` 前缀
- **头文件守卫**:统一为 `MEG_*_H_` 形式,保持一致性
## 开发环境

@ -11,8 +11,8 @@ else
endif
TARGET := math_exam_generator
SRCS := main.cpp app.cpp auth.cpp exam.cpp login.cpp utils.cpp
OBJS := $(SRCS:.cpp=.o)
SRCS := main.cc app.cc auth.cc exam.cc login.cc utils.cc
OBJS := $(SRCS:.cc=.o)
all: $(TARGET)
@ -20,7 +20,7 @@ $(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) -o $@ $^
-$(RM) $(OBJS)
%.o: %.cpp
%.o: %.cc
$(CXX) $(CXXFLAGS) -c $< -o $@
.PHONY: clean

@ -0,0 +1,121 @@
#include "include/app.h"
#include "include/utils.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <cctype>
#include <algorithm>
namespace fs = std::filesystem;
namespace meg {
// 应用程序构造函数:
// 初始化题目生成器的具体实现(通过抽象接口进行多态管理)
App::App() {
exam_ = std::make_unique<ExamGenerator>();
}
// 处理登录后交互流程:
// - 支持用户通过命令切换题目难度(小学/初中/高中)
// - 显式校验数字输入并生成试卷,避免使用异常进行控制流
void App::handle_logged_in() {
if (!current_user_) return;
current_level_ = current_user_->level;
while (true) {
// 检测是否发生了 Ctrl+C 中断,若发生则优雅退出当前交互
if (WasInterrupted()) {
std::wcout << L"\n已取消Ctrl+C返回主菜单或退出。" << '\n';
// 关键修复:重置当前用户,避免 run() 立即再次进入 handle_logged_in 导致重复提示
current_user_.reset();
ClearInterrupted();
return;
}
std::wcout << L"准备生成" << LevelToChinese(current_level_) << L"数学题目,请输入生成题目数量(输入-1将退出当前用户重新登录";
std::wcout.flush();
std::wstring line;
// 当读取失败EOF/中断)时,退出登录,避免重复打印提示
if (!std::getline(std::wcin, line)) {
if (WasInterrupted()) {
std::wcout << L"\n已取消Ctrl+C已退出登录。" << '\n';
ClearInterrupted();
} else {
std::wcout << L"\n输入终止,已退出登录。" << '\n';
}
current_user_.reset();
return;
}
line = Trim(line);
if (line.empty()) continue;
if (line == L"-1") {
std::wcout << L"已退出登录。" << '\n';
current_user_.reset();
return;
}
// 支持命令:切换为小学/初中/高中
if (StartsWith(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 << '\n';
continue;
}
std::wcout << L"已切换为" << LevelToChinese(current_level_) << L"" << '\n';
continue;
}
// 显式校验输入为数字,避免异常
std::string ascii(line.begin(), line.end());
bool all_digits = !ascii.empty() && std::all_of(ascii.begin(), ascii.end(), [](unsigned char c){ return std::isdigit(c); });
if (!all_digits) {
std::wcout << L"请输入正确的数字,或使用指令如:切换为初中。" << '\n';
continue;
}
int n = std::stoi(ascii);
if (n < 10 || n > 30) {
std::wcout << L"请输入 10-30 之间的数字。" << '\n';
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_->GenerateUnique(current_level_, n, history);
// 保存试卷
std::string ts = NowTimestampStr();
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() << '\n';
}
}
void App::run() {
InitConsoleLocale();
std::wcout << L"中小学数学卷子自动生成程序" << '\n';
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

@ -1,92 +0,0 @@
#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

@ -1,4 +1,4 @@
#include "include/auth.hpp"
#include "include/auth.h"
namespace meg {

@ -1,4 +1,4 @@
#include "include/exam.hpp"
#include "include/exam.h"
#include <fstream>
#include <iostream>
@ -33,14 +33,15 @@ void ExamGenerator::append_history(const std::filesystem::path& history_path, co
}
}
std::vector<std::string> ExamGenerator::generate_unique(Level level, int count, std::unordered_set<std::string>& history) {
// 生成不重复题目:按难度生成并去重,限制尝试次数避免极端情况
std::vector<std::string> ExamGenerator::GenerateUnique(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 (level == Level::Primary) q = GenPrimary();
else if (level == Level::Middle) q = GenMiddle();
else q = GenHigh();
if (history.insert(q).second) {
out.push_back(q);
@ -50,7 +51,8 @@ std::vector<std::string> ExamGenerator::generate_unique(Level level, int count,
return out;
}
static std::string join_ops(const std::vector<int>& nums, const std::vector<char>& ops) {
// 将数字与运算符拼接为表达式字符串
static std::string JoinOps(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]);
@ -63,37 +65,33 @@ static std::string join_ops(const std::vector<int>& nums, const std::vector<char
return s;
}
std::string ExamGenerator::gen_primary() {
int n_ops = rand_int(rng_, 1, 2); // 2-3个操作数 => 1-2个操作符
// 生成小学题目2-3 个操作数的四则运算
// 生成小学题目1-5 个操作数的四则运算
std::string ExamGenerator::GenPrimary() {
int operands = rand_int(rng_, 1, 5);
int n_ops = operands > 0 ? operands - 1 : 0;
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[] = {'+', '-', '*', '/'};
for (int i = 0; i < operands; ++i) nums.push_back(rand_int(rng_, 1, 100));
static const char kAllOps[] = {'+', '-', '*', '/'};
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 + " = ?";
for (int i = 0; i < n_ops; ++i) ops.push_back(kAllOps[rand_int(rng_, 0, 3)]);
std::string s = JoinOps(nums, ops);
return s + " = ?";
}
std::string ExamGenerator::gen_middle() {
int n_ops = rand_int(rng_, 2, 3); // 3-4 操作数
// 生成初中题目1-5 个操作数,随机将一个数变为平方或开方
std::string ExamGenerator::GenMiddle() {
int operands = rand_int(rng_, 1, 5);
int n_ops = operands > 0 ? operands - 1 : 0;
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[] = {'+', '-', '*', '/'};
for (int i = 0; i < operands; ++i) nums.push_back(rand_int(rng_, 1, 100));
static const char kAllOps[] = {'+', '-', '*', '/'};
std::vector<char> ops;
for (int i = 0; i < n_ops; ++i) ops.push_back(all_ops[rand_int(rng_, 0, 3)]);
for (int i = 0; i < n_ops; ++i) ops.push_back(kAllOps[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 expr = JoinOps(nums, ops);
// 找到替换位置(简单方式:直接重新拼接)
std::string s;
for (size_t i = 0; i < nums.size(); ++i) {
@ -110,26 +108,25 @@ std::string ExamGenerator::gen_middle() {
return s + " = ?";
}
std::string ExamGenerator::gen_high() {
int n_ops = rand_int(rng_, 3, 4); // 4-5 操作数
// 生成高中题目1-5 个操作数,至少包含一个三角函数项,并可能整体平方或开方
std::string ExamGenerator::GenHigh() {
int operands = rand_int(rng_, 1, 5);
int n_ops = operands > 0 ? operands - 1 : 0;
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[] = {'+', '-', '*', '/'};
for (int i = 0; i < operands; ++i) nums.push_back(rand_int(rng_, 1, 100));
static const char kAllOps[] = {'+', '-', '*', '/'};
std::vector<char> ops;
for (int i = 0; i < n_ops; ++i) ops.push_back(all_ops[rand_int(rng_, 0, 3)]);
for (int i = 0; i < n_ops; ++i) ops.push_back(kAllOps[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)";
static const int kAngles[] = {30, 45, 60, 90};
std::string tri = std::string(funcs[rand_int(rng_, 0, 2)]) + "(" + std::to_string(kAngles[rand_int(rng_, 0, 3)]) + "deg)";
// 随机把一个数替换为三角项
size_t replace_pos = rand_int(rng_, 0, (int)nums.size() - 1);
// 重建字符串并替换
@ -139,7 +136,6 @@ std::string ExamGenerator::gen_high() {
s += term;
if (i < ops.size()) { s += ' '; s += ops[i]; s += ' '; }
}
// 同时可能再加平方或开方
if (rand_int(rng_, 0, 1)) {
s = "(" + s + ")^2";

@ -1,11 +1,12 @@
#ifndef MEG_APP_HPP
#define MEG_APP_HPP
#ifndef MEG_APP_H_
#define MEG_APP_H_
#include <optional>
#include <memory>
#include <filesystem>
#include "auth.hpp"
#include "login.hpp"
#include "exam.hpp"
#include "auth.h"
#include "login.h"
#include "exam.h"
namespace meg {
@ -17,7 +18,8 @@ public:
private:
Auth auth_;
LoginManager login_{auth_};
ExamGenerator exam_;
// 使用多态:题目生成器接口指针,便于后续替换实现
std::unique_ptr<IExamGenerator> exam_;
std::optional<User> current_user_;
Level current_level_ = Level::Primary;
@ -27,4 +29,4 @@ private:
} // namespace meg
#endif // MEG_APP_HPP
#endif // MEG_APP_H_

@ -1,10 +1,10 @@
#ifndef MEG_AUTH_HPP
#define MEG_AUTH_HPP
#ifndef MEG_AUTH_H_
#define MEG_AUTH_H_
#include <string>
#include <optional>
#include <map>
#include "utils.hpp"
#include "utils.h"
namespace meg {
@ -24,4 +24,4 @@ private:
} // namespace meg
#endif // MEG_AUTH_HPP
#endif // MEG_AUTH_H_

@ -0,0 +1,50 @@
#ifndef MEG_EXAM_H_
#define MEG_EXAM_H_
#include <string>
#include <vector>
#include <unordered_set>
#include <filesystem>
#include <random>
#include "utils.h"
namespace meg {
// 抽象题目生成器接口:定义题目生成与历史管理的统一协议
class IExamGenerator {
public:
virtual ~IExamGenerator() = default;
// 加载历史:用于避免重复题目
virtual std::unordered_set<std::string> load_history(const std::filesystem::path& history_path) = 0;
// 追加历史:将本次生成写入历史库
virtual void append_history(const std::filesystem::path& history_path, const std::vector<std::string>& qs) = 0;
// 生成不重复题目:按难度与数量生成题目,排除历史
virtual std::vector<std::string> GenerateUnique(Level level, int count, std::unordered_set<std::string>& history) = 0;
};
// 具体题目生成器实现:使用随机数生成不同难度的题目
class ExamGenerator : public IExamGenerator {
public:
ExamGenerator();
// 加载历史:从文件读取每行题目为历史集合
std::unordered_set<std::string> load_history(const std::filesystem::path& history_path) override;
// 追加历史:将题目列表逐行追加到文件末尾
void append_history(const std::filesystem::path& history_path, const std::vector<std::string>& qs) override;
// 生成不重复题目:依据难度选择生成函数并去重
std::vector<std::string> GenerateUnique(Level level, int count, std::unordered_set<std::string>& history) override;
private:
std::mt19937 rng_;
// 生成小学难度题目
std::string GenPrimary();
// 生成初中难度题目
std::string GenMiddle();
// 生成高中难度题目
std::string GenHigh();
};
} // namespace meg
#endif // MEG_EXAM_H_

@ -1,33 +0,0 @@
#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

@ -1,9 +1,9 @@
#ifndef MEG_LOGIN_HPP
#define MEG_LOGIN_HPP
#ifndef MEG_LOGIN_H_
#define MEG_LOGIN_H_
#include <optional>
#include <string>
#include "auth.hpp"
#include "auth.h"
namespace meg {
@ -17,4 +17,4 @@ private:
} // namespace meg
#endif // MEG_LOGIN_HPP
#endif // MEG_LOGIN_H_

@ -0,0 +1,40 @@
#ifndef MEG_UTILS_H_
#define MEG_UTILS_H_
#include <string>
#include <filesystem>
namespace meg {
enum class Level { Primary, Middle, High };
// 将难度枚举转换为中文
std::wstring LevelToChinese(Level level);
// 初始化控制台本地化,解决中文输入/输出问题
// 初始化控制台本地化,解决中文输入/输出问题
void InitConsoleLocale();
// 生成时间戳YYYY-MM-DD-HH-MM-SS
// 生成时间戳YYYY-MM-DD-HH-MM-SS
std::string NowTimestampStr();
// 字符串工具
// 字符串工具:判断前缀与去除首尾空白
bool StartsWith(const std::wstring& s, const std::wstring& prefix);
std::wstring Trim(const std::wstring& s);
// 安装 Ctrl+C 等中断信号处理器(平台兼容)
// 作用:拦截用户在输入过程中按下 Ctrl+C设置中断标志以便程序优雅退出
void InstallSignalHandlers();
// 查询是否发生了中断(例如 Ctrl+C
// 返回值true 表示已检测到中断,需要尽快结束当前交互或程序
bool WasInterrupted();
// 清除中断标志(在完成清理或打印提示后调用)
void ClearInterrupted();
} // namespace meg
#endif // MEG_UTILS_H_

@ -1,25 +0,0 @@
#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

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -1,11 +1,12 @@
#include "include/login.hpp"
#include "include/utils.hpp"
#include "include/login.h"
#include "include/utils.h"
#include <iostream>
#include <sstream>
namespace meg {
// 登录交互:读取“用户名 密码”验证成功返回用户EOF 返回空
std::optional<User> LoginManager::prompt_login() {
while (true) {
std::wcout << L"请输入用户名和密码(用空格隔开): ";
@ -14,7 +15,7 @@ std::optional<User> LoginManager::prompt_login() {
if (!std::getline(std::wcin, line)) {
return std::nullopt;
}
line = trim(line);
line = Trim(line);
if (line.empty()) continue;
// 拆分为两段:用户名 密码
@ -22,15 +23,15 @@ std::optional<User> LoginManager::prompt_login() {
std::wstring u, p;
iss >> u >> p;
if (u.empty() || p.empty()) {
std::wcout << L"输入格式不正确,请重新输入。" << std::endl;
std::wcout << L"输入格式不正确,请重新输入。" << '\n';
continue;
}
auto user = auth_.authenticate(u, p);
if (user) {
std::wcout << L"登录成功,欢迎您," << u << L"" << std::endl;
std::wcout << L"登录成功,欢迎您," << u << L"" << '\n';
return user;
} else {
std::wcout << L"用户名或密码错误,请重试。" << std::endl;
std::wcout << L"用户名或密码错误,请重试。" << '\n';
}
}
}

@ -0,0 +1,10 @@
#include "include/app.h"
#include "include/utils.h"
int main() {
// 主程序入口:安装 Ctrl+C 中断处理,确保交互过程优雅退出
meg::InstallSignalHandlers();
meg::App app;
app.run();
return 0;
}

@ -1,7 +0,0 @@
#include "include/app.hpp"
int main() {
meg::App app;
app.run();
return 0;
}

Binary file not shown.

@ -1,30 +1,180 @@
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 = ?
7 + 2 = ?
(16 * 15) + 25 = ?
(35 - 33) + 38 = ?
3 - (32 * 47) = ?
(15 + 4) + 26 = ?
36 / 30 = ?
50 / (6 - 11) = ?
(3 + 25) + 3 = ?
14 / 12 = ?
26 + 1 = ?
43 * (15 + 8) = ?
30 * 10 = ?
41 / 37 = ?
38 + (30 / 28) = ?
(38 * 27) * 45 = ?
5 / 8 = ?
43 - 35 = ?
(6 / 39) + 44 = ?
(47 / 6) * 40 = ?
5 - 43 = ?
(11 - 44) - 1 = ?
30 - 36 = ?
(44 + 43) / 38 = ?
11 - 29 = ?
2 - (18 - 17) = ?
14 * (33 - 10) = ?
46 - 27 = ?
11 - 15 = ?
39 / 4 = ?
10 + (41 / 45) = ?
46 = ?
34 = ?
47 / 37 / 27 * 20 = ?
43 - 13 = ?
35 + 49 - 40 = ?
1 = ?
48 + 50 / 15 / 31 = ?
13 - 21 - 19 = ?
3 / 29 * 45 + 40 = ?
48 = ?
40 * 43 / 27 - 28 = ?
30 = ?
1 - 4 = ?
24 = ?
35 = ?
1 * 38 / 28 = ?
11 = ?
50 - 14 - 49 = ?
14 / 1 = ?
44 - 35 / 14 + 12 * 24 = ?
50 + 48 / 49 - 23 + 35 = ?
46 + 10 * 7 + 45 + 44 = ?
43 + 32 = ?
20 * 17 + 2 + 26 = ?
8 * 4 - 17 + 33 = ?
49 - 40 / 27 + 42 = ?
42 + 24 + 42 * 6 / 8 = ?
36 * 18 - 12 = ?
11 - 31 = ?
19 - 5 = ?
22 = ?
71 * 95 / 30 = ?
100 * 14 / 30 + 26 = ?
15 + 9 = ?
25 = ?
86 = ?
66 / 58 / 97 * 19 = ?
33 - 99 = ?
5 = ?
93 + 90 + 79 / 73 = ?
33 / 73 - 12 / 100 / 31 = ?
59 / 81 / 48 * 34 * 95 = ?
21 * 38 - 87 / 46 - 3 = ?
82 + 40 - 4 / 41 * 5 = ?
9 - 17 = ?
67 * 49 / 34 + 85 * 31 = ?
86 / 43 = ?
89 * 41 = ?
26 + 4 - 29 / 59 = ?
2 = ?
85 * 60 * 1 = ?
11 - 17 / 72 = ?
83 + 5 * 58 + 92 + 68 = ?
21 - 76 + 88 + 89 / 89 = ?
62 - 79 = ?
76 * 87 - 86 / 81 = ?
22 / 22 * 78 * 36 = ?
18 - 70 / 40 * 47 = ?
46 - 26 = ?
47 / 59 / 31 * 80 - 92 = ?
sqrt(sin(30deg)) = ?
(tan(45deg) * 27 + 60)^2 = ?
sqrt(34 + cos(90deg) / 67) = ?
24 - cos(90deg) / 70 = ?
sqrt(92 * sin(90deg)) = ?
(86 * 8 * 20 * cos(45deg) - 53)^2 = ?
sqrt(58 - 1 + sin(45deg) / 87) = ?
(sin(90deg) / 75)^2 = ?
(97 - 88 * 6 * cos(60deg) + 79)^2 = ?
(15 / cos(90deg) / 40 * 7 / 7)^2 = ?
sqrt(93 + sin(90deg) * 95 - 21) = ?
82 / tan(60deg) + 87 + 86 = ?
(tan(90deg))^2 = ?
(72 / sin(60deg) / 27 - 37 * 96)^2 = ?
sqrt(cos(30deg)) = ?
(59 + 2 * 16 * 5 - sin(30deg))^2 = ?
sqrt(35 + 16 * 77 + tan(30deg) / 81) = ?
sqrt(sin(60deg)) = ?
sqrt(cos(60deg) / 23 + 60 / 75 * 63) = ?
58 / 45 / cos(30deg) + 9 * 40 = ?
sqrt(69 * sin(30deg) * 74) = ?
(54 + 76 - 14 * cos(30deg) + 45)^2 = ?
(43 * cos(45deg) / 8 * 70)^2 = ?
(47 / sin(90deg))^2 = ?
66 - 18 + 93 / sin(45deg) + 33 = ?
(cos(45deg) + 2 / 98 + 50)^2 = ?
(68 * 94 / 47 + sin(45deg))^2 = ?
cos(60deg) = ?
sqrt(92 - 38 + cos(90deg) / 54) = ?
72 + 71 + cos(30deg) * 100 = ?
53 - 93 / 33 + cos(60deg) - 35 = ?
(97 + sin(90deg))^2 = ?
(79 * 37 - tan(60deg) * 41 / 40)^2 = ?
(cos(60deg))^2 = ?
(85 * 40 * 91 - tan(90deg))^2 = ?
9 + tan(45deg) + 27 = ?
sqrt(72 + sin(30deg) / 37) = ?
tan(90deg) + 20 / 31 - 67 = ?
sqrt(98 * 94 / cos(60deg)) = ?
tan(45deg) = ?
sqrt(cos(30deg) * 20 + 36) = ?
(38 - 98 + 11 / sin(45deg))^2 = ?
(sin(90deg))^2 = ?
(42 * sin(60deg) + 56)^2 = ?
sqrt(38 / 11 * sin(60deg)) = ?
sqrt(cos(60deg)) = ?
(13 * cos(45deg) + 22 - 99 + 94)^2 = ?
(11 - 74 * cos(30deg))^2 = ?
(73 / tan(60deg) / 14)^2 = ?
sqrt(tan(90deg) - 80 * 18 - 76 - 26) = ?
(22 / cos(60deg) * 99 + 7)^2 = ?
76 / 86 + tan(90deg) = ?
(90 + tan(60deg) - 70)^2 = ?
sqrt(tan(90deg)) = ?
87 + 4 * cos(45deg) / 19 * 91 = ?
(tan(90deg) - 59)^2 = ?
sqrt(8 + 7 - 14 - cos(90deg) + 69) = ?
(tan(30deg))^2 = ?
(sin(60deg))^2 = ?
sin(90deg) + 92 = ?
14 - 61 * 93 = ?
70 / 58 / 41 = ?
45 = ?
43 * 22 / 37 - 93 = ?
70 / 5 * 87 + 11 - 64 = ?
7 - 64 - 7 / 32 = ?
64 + 47 = ?
88 - 97 + 40 - 82 * 19 = ?
100 = ?
18 = ?
70 = ?
46 / 45 = ?
19 * 53 + 71 - 51 = ?
78 * 32 = ?
98 + 92 * 42 * 65 = ?
78 * 40 + 77 = ?
16 + 63 * 2 + 22 * 37 = ?
3 * 2 * 94 / 79 - 73 = ?
44 - 4 = ?
20 / 54 / 66 = ?
60 / 68 / 17 + 76 = ?
7 + 16 * 67 = ?
12 = ?
3 = ?
99 / 61 + 5 = ?
95 * 64 = ?
94 = ?
26 + 67 * 29 = ?
49 / 83 * 77 / 69 = ?
9 * 45 + 6 * 81 = ?

@ -1,60 +0,0 @@
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 = ?

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

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

@ -0,0 +1,60 @@
1. 22 = ?
2. 71 * 95 / 30 = ?
3. 100 * 14 / 30 + 26 = ?
4. 15 + 9 = ?
5. 25 = ?
6. 86 = ?
7. 66 / 58 / 97 * 19 = ?
8. 33 - 99 = ?
9. 5 = ?
10. 93 + 90 + 79 / 73 = ?
11. 33 / 73 - 12 / 100 / 31 = ?
12. 59 / 81 / 48 * 34 * 95 = ?
13. 21 * 38 - 87 / 46 - 3 = ?
14. 82 + 40 - 4 / 41 * 5 = ?
15. 9 - 17 = ?
16. 67 * 49 / 34 + 85 * 31 = ?
17. 86 / 43 = ?
18. 89 * 41 = ?
19. 26 + 4 - 29 / 59 = ?
20. 2 = ?
21. 85 * 60 * 1 = ?
22. 11 - 17 / 72 = ?
23. 83 + 5 * 58 + 92 + 68 = ?
24. 21 - 76 + 88 + 89 / 89 = ?
25. 62 - 79 = ?
26. 76 * 87 - 86 / 81 = ?
27. 22 / 22 * 78 * 36 = ?
28. 18 - 70 / 40 * 47 = ?
29. 46 - 26 = ?
30. 47 / 59 / 31 * 80 - 92 = ?

@ -0,0 +1,60 @@
1. sqrt(sin(30deg)) = ?
2. (tan(45deg) * 27 + 60)^2 = ?
3. sqrt(34 + cos(90deg) / 67) = ?
4. 24 - cos(90deg) / 70 = ?
5. sqrt(92 * sin(90deg)) = ?
6. (86 * 8 * 20 * cos(45deg) - 53)^2 = ?
7. sqrt(58 - 1 + sin(45deg) / 87) = ?
8. (sin(90deg) / 75)^2 = ?
9. (97 - 88 * 6 * cos(60deg) + 79)^2 = ?
10. (15 / cos(90deg) / 40 * 7 / 7)^2 = ?
11. sqrt(93 + sin(90deg) * 95 - 21) = ?
12. 82 / tan(60deg) + 87 + 86 = ?
13. (tan(90deg))^2 = ?
14. (72 / sin(60deg) / 27 - 37 * 96)^2 = ?
15. sqrt(cos(30deg)) = ?
16. (59 + 2 * 16 * 5 - sin(30deg))^2 = ?
17. sqrt(35 + 16 * 77 + tan(30deg) / 81) = ?
18. sqrt(sin(60deg)) = ?
19. sqrt(cos(60deg) / 23 + 60 / 75 * 63) = ?
20. 58 / 45 / cos(30deg) + 9 * 40 = ?
21. sqrt(69 * sin(30deg) * 74) = ?
22. (54 + 76 - 14 * cos(30deg) + 45)^2 = ?
23. (43 * cos(45deg) / 8 * 70)^2 = ?
24. (47 / sin(90deg))^2 = ?
25. 66 - 18 + 93 / sin(45deg) + 33 = ?
26. (cos(45deg) + 2 / 98 + 50)^2 = ?
27. (68 * 94 / 47 + sin(45deg))^2 = ?
28. cos(60deg) = ?
29. sqrt(92 - 38 + cos(90deg) / 54) = ?
30. 72 + 71 + cos(30deg) * 100 = ?

@ -0,0 +1,60 @@
1. 53 - 93 / 33 + cos(60deg) - 35 = ?
2. (97 + sin(90deg))^2 = ?
3. (79 * 37 - tan(60deg) * 41 / 40)^2 = ?
4. (cos(60deg))^2 = ?
5. (85 * 40 * 91 - tan(90deg))^2 = ?
6. 9 + tan(45deg) + 27 = ?
7. sqrt(72 + sin(30deg) / 37) = ?
8. tan(90deg) + 20 / 31 - 67 = ?
9. sqrt(98 * 94 / cos(60deg)) = ?
10. tan(45deg) = ?
11. sqrt(cos(30deg) * 20 + 36) = ?
12. (38 - 98 + 11 / sin(45deg))^2 = ?
13. (sin(90deg))^2 = ?
14. (42 * sin(60deg) + 56)^2 = ?
15. sqrt(38 / 11 * sin(60deg)) = ?
16. sqrt(cos(60deg)) = ?
17. (13 * cos(45deg) + 22 - 99 + 94)^2 = ?
18. (11 - 74 * cos(30deg))^2 = ?
19. (73 / tan(60deg) / 14)^2 = ?
20. sqrt(tan(90deg) - 80 * 18 - 76 - 26) = ?
21. (22 / cos(60deg) * 99 + 7)^2 = ?
22. 76 / 86 + tan(90deg) = ?
23. (90 + tan(60deg) - 70)^2 = ?
24. sqrt(tan(90deg)) = ?
25. 87 + 4 * cos(45deg) / 19 * 91 = ?
26. (tan(90deg) - 59)^2 = ?
27. sqrt(8 + 7 - 14 - cos(90deg) + 69) = ?
28. (tan(30deg))^2 = ?
29. (sin(60deg))^2 = ?
30. sin(90deg) + 92 = ?

@ -0,0 +1,60 @@
1. 14 - 61 * 93 = ?
2. 70 / 58 / 41 = ?
3. 45 = ?
4. 43 * 22 / 37 - 93 = ?
5. 70 / 5 * 87 + 11 - 64 = ?
6. 7 - 64 - 7 / 32 = ?
7. 64 + 47 = ?
8. 88 - 97 + 40 - 82 * 19 = ?
9. 100 = ?
10. 18 = ?
11. 70 = ?
12. 46 / 45 = ?
13. 19 * 53 + 71 - 51 = ?
14. 78 * 32 = ?
15. 98 + 92 * 42 * 65 = ?
16. 78 * 40 + 77 = ?
17. 16 + 63 * 2 + 22 * 37 = ?
18. 3 * 2 * 94 / 79 - 73 = ?
19. 44 - 4 = ?
20. 20 / 54 / 66 = ?
21. 60 / 68 / 17 + 76 = ?
22. 7 + 16 * 67 = ?
23. 12 = ?
24. 3 = ?
25. 99 / 61 + 5 = ?
26. 95 * 64 = ?
27. 94 = ?
28. 26 + 67 * 29 = ?
29. 49 / 83 * 77 / 69 = ?
30. 9 * 45 + 6 * 81 = ?

@ -0,0 +1,125 @@
#include "include/utils.h"
#include <locale>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <chrono>
#include <algorithm>
#include <atomic>
#include <csignal>
#ifdef _WIN32
# include <io.h>
# include <fcntl.h>
# include <windows.h>
#endif
namespace meg {
// 将难度枚举转换为中文
std::wstring LevelToChinese(Level level) {
switch (level) {
case Level::Primary: return L"小学";
case Level::Middle: return L"初中";
case Level::High: return L"高中";
}
return L"小学";
}
// 初始化控制台本地化,解决中文输入/输出问题
void InitConsoleLocale() {
#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
}
// 生成时间戳YYYY-MM-DD-HH-MM-SS
std::string NowTimestampStr() {
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 StartsWith(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 IsSpace(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 && IsSpace(s[b])) ++b;
while (e > b && IsSpace(s[e-1])) --e;
return s.substr(b, e - b);
}
// 全局原子标志:记录是否被用户通过 Ctrl+C 等中断
static std::atomic<bool> g_interrupted{false};
#ifdef _WIN32
// Windows 控制台中断处理:拦截 CTRL+C/CTRL+BREAK/关闭事件
// 返回 TRUE 表示已处理,避免系统默认直接终止进程
static BOOL WINAPI ConsoleCtrlHandler(DWORD type) {
switch (type) {
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
case CTRL_CLOSE_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
g_interrupted.store(true);
return TRUE;
default:
return FALSE;
}
}
// 安装平台相关的信号/控制台处理器
void InstallSignalHandlers() {
SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
}
#else
// POSIX 信号处理:拦截 SIGINTCtrl+C
static void SigIntHandler(int) {
g_interrupted.store(true);
}
// 安装平台相关的信号/控制台处理器
void InstallSignalHandlers() {
std::signal(SIGINT, SigIntHandler);
}
#endif
// 查询是否被中断
bool WasInterrupted() { return g_interrupted.load(); }
// 清除中断标志
void ClearInterrupted() { g_interrupted.store(false); }
} // namespace meg

@ -1,74 +0,0 @@
#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