|
|
|
|
@ -0,0 +1,520 @@
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <set>
|
|
|
|
|
#include <random>
|
|
|
|
|
#include <ctime>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <iomanip>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <filesystem>
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
#include <limits>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
|
|
|
|
|
namespace math_test_generator {
|
|
|
|
|
|
|
|
|
|
// 用户账户结构体(严格匹配文档附表1:小学/初中/高中各3个账户)
|
|
|
|
|
struct UserAccount {
|
|
|
|
|
std::string username; // 用户名(如"李四2")
|
|
|
|
|
std::string password; // 密码(统一为"123")
|
|
|
|
|
std::string user_type; // 账户类型(小学/初中/高中)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class MathTestGenerator {
|
|
|
|
|
public:
|
|
|
|
|
// 构造函数:初始化随机数与预设账户(符合文档初始化需求)
|
|
|
|
|
MathTestGenerator() : is_logged_in_(false) {
|
|
|
|
|
std::random_device rd;
|
|
|
|
|
rng_.seed(rd());
|
|
|
|
|
InitializePresetAccounts();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 程序主入口(按文档流程:登录→输入数量→生成/切换→保存)
|
|
|
|
|
void Run() {
|
|
|
|
|
std::cout << "=== 中小学数学卷子自动生成程序 ===" << std::endl;
|
|
|
|
|
while (true) {
|
|
|
|
|
if (!is_logged_in_) {
|
|
|
|
|
HandleLogin(); // 未登录:处理用户名密码输入
|
|
|
|
|
} else {
|
|
|
|
|
HandleLoggedInOperations(); // 已登录:处理题目生成
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// 成员变量(按文档需求定义)
|
|
|
|
|
std::vector<UserAccount> preset_accounts_; // 预设账户(文档附表1)
|
|
|
|
|
UserAccount current_user_; // 当前登录用户
|
|
|
|
|
bool is_logged_in_; // 登录状态标记
|
|
|
|
|
std::map<std::string, std::set<std::string>> question_history_; // 题目去重历史(文档需求3)
|
|
|
|
|
std::mt19937 rng_; // 随机数生成器
|
|
|
|
|
|
|
|
|
|
// 常量(严格匹配文档需求3、5)
|
|
|
|
|
static constexpr int kMinQuestionCount = 10; // 最小题目数(文档需求3)
|
|
|
|
|
static constexpr int kMaxQuestionCount = 30; // 最大题目数(文档需求3)
|
|
|
|
|
static constexpr int kExitLoginCode = -1; // 退出登录指令(文档需求2)
|
|
|
|
|
static constexpr int kMaxRetryCount = 1000; // 题目去重最大尝试次数
|
|
|
|
|
|
|
|
|
|
// 初始化预设账户(文档附表1:小学3个/初中3个/高中3个)
|
|
|
|
|
void InitializePresetAccounts() {
|
|
|
|
|
// 小学账户
|
|
|
|
|
preset_accounts_.push_back({"张三1", "123", "小学"});
|
|
|
|
|
preset_accounts_.push_back({"张三2", "123", "小学"});
|
|
|
|
|
preset_accounts_.push_back({"张三3", "123", "小学"});
|
|
|
|
|
// 初中账户
|
|
|
|
|
preset_accounts_.push_back({"李四1", "123", "初中"});
|
|
|
|
|
preset_accounts_.push_back({"李四2", "123", "初中"});
|
|
|
|
|
preset_accounts_.push_back({"李四3", "123", "初中"});
|
|
|
|
|
// 高中账户
|
|
|
|
|
preset_accounts_.push_back({"王五1", "123", "高中"});
|
|
|
|
|
preset_accounts_.push_back({"王五2", "123", "高中"});
|
|
|
|
|
preset_accounts_.push_back({"王五3", "123", "高中"});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理登录(文档需求1:命令行输入用户名密码,空格分隔)
|
|
|
|
|
void HandleLogin() {
|
|
|
|
|
std::string username, password;
|
|
|
|
|
std::cout << "请输入用户名和密码(用空格隔开): ";
|
|
|
|
|
// 处理输入格式错误
|
|
|
|
|
while (!(std::cin >> username >> password)) {
|
|
|
|
|
std::cin.clear();
|
|
|
|
|
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
|
|
|
|
std::cout << "输入格式错误,请重新输入用户名和密码(用空格隔开): ";
|
|
|
|
|
}
|
|
|
|
|
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
|
|
|
|
|
|
|
|
|
// 验证登录并反馈(文档需求1:成功则显示"当前选择为XX出题")
|
|
|
|
|
if (VerifyLogin(username, password)) {
|
|
|
|
|
std::cout << "登录成功!当前选择为" << current_user_.user_type << "出题" << std::endl;
|
|
|
|
|
} else {
|
|
|
|
|
std::cout << "请输入正确的用户名、密码" << std::endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 登录验证(文档需求1:匹配成功后预创建用户文件夹,符合需求5)
|
|
|
|
|
bool VerifyLogin(const std::string& username, const std::string& password) {
|
|
|
|
|
for (const auto& account : preset_accounts_) {
|
|
|
|
|
if (account.username == username && account.password == password) {
|
|
|
|
|
current_user_ = account;
|
|
|
|
|
is_logged_in_ = true;
|
|
|
|
|
// 登录成功后立即创建用户文件夹(文档需求5:每个账号一个文件夹)
|
|
|
|
|
std::string user_dir = GetUserDirectory();
|
|
|
|
|
if (CreateUserDirectory(user_dir)) {
|
|
|
|
|
LoadQuestionHistory(); // 加载历史题目(文档需求3去重)
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 已登录操作(文档需求2/3/4:输入数量、切换类型、生成题目)
|
|
|
|
|
void HandleLoggedInOperations() {
|
|
|
|
|
int question_count = 0;
|
|
|
|
|
// 提示输入题目数量(文档需求2:显示"准备生成XX数学题目")
|
|
|
|
|
std::cout << "准备生成" << current_user_.user_type
|
|
|
|
|
<< "数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录): ";
|
|
|
|
|
// 处理非数字输入
|
|
|
|
|
while (!(std::cin >> question_count)) {
|
|
|
|
|
std::cin.clear();
|
|
|
|
|
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
|
|
|
|
std::cout << "输入格式错误,请输入有效数字(10-30或-1): ";
|
|
|
|
|
}
|
|
|
|
|
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
|
|
|
|
|
|
|
|
|
// 退出登录(文档需求2:输入-1退出)
|
|
|
|
|
if (question_count == kExitLoginCode) {
|
|
|
|
|
Logout();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 验证题目数量(文档需求3:10-30含边界)
|
|
|
|
|
if (!IsValidQuestionCount(question_count)) {
|
|
|
|
|
std::cout << "题目数量有效范围是10-30,请重新输入" << std::endl;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理类型切换(文档需求4:输入"切换为XX"切换类型)
|
|
|
|
|
std::string command;
|
|
|
|
|
std::cout << "请输入命令(直接回车生成题目,或输入'切换为XX'切换类型): ";
|
|
|
|
|
std::getline(std::cin, command);
|
|
|
|
|
|
|
|
|
|
if (!command.empty() && !HandleTypeSwitch(command)) {
|
|
|
|
|
std::cout << "切换失败,将按原类型生成题目" << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成并保存试卷(文档需求3/5:不重复题目、账号文件夹存储)
|
|
|
|
|
GenerateAndSaveTestPaper(question_count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 验证题目数量(文档需求3)
|
|
|
|
|
bool IsValidQuestionCount(int count) const {
|
|
|
|
|
return count >= kMinQuestionCount && count <= kMaxQuestionCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理类型切换(文档需求4:仅支持小学/初中/高中)
|
|
|
|
|
bool HandleTypeSwitch(const std::string& command) {
|
|
|
|
|
const std::string kSwitchPrefix = "切换为";
|
|
|
|
|
// 检查命令格式
|
|
|
|
|
if (command.substr(0, kSwitchPrefix.size()) != kSwitchPrefix) {
|
|
|
|
|
std::cout << "请输入小学、初中和高中三个选项中的一个" << std::endl;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 提取并验证目标类型
|
|
|
|
|
std::string target_type = command.substr(kSwitchPrefix.size());
|
|
|
|
|
if (target_type != "小学" && target_type != "初中" && target_type != "高中") {
|
|
|
|
|
std::cout << "请输入小学、初中和高中三个选项中的一个" << std::endl;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 切换类型并提示(文档需求4)
|
|
|
|
|
current_user_.user_type = target_type;
|
|
|
|
|
std::cout << "系统提示:准备生成" << target_type << "数学题目,请输入生成题目数量" << std::endl;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成试卷并保存(文档需求3/5)
|
|
|
|
|
void GenerateAndSaveTestPaper(int question_count) {
|
|
|
|
|
std::vector<std::string> questions;
|
|
|
|
|
int retry_count = 0;
|
|
|
|
|
|
|
|
|
|
// 生成不重复题目(文档需求3:同一老师题目不重复)
|
|
|
|
|
while (questions.size() < static_cast<size_t>(question_count) && retry_count < kMaxRetryCount) {
|
|
|
|
|
std::string question = GenerateQuestionByType(current_user_.user_type);
|
|
|
|
|
if (!IsQuestionDuplicate(question)) {
|
|
|
|
|
questions.push_back(question);
|
|
|
|
|
AddQuestionToHistory(question);
|
|
|
|
|
}
|
|
|
|
|
retry_count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理生成不足问题
|
|
|
|
|
if (questions.size() < static_cast<size_t>(question_count)) {
|
|
|
|
|
std::cerr << "警告:无法生成足够的不重复题目,仅生成" << questions.size() << "道" << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 保存题目(文档需求5:按格式保存到账号文件夹)
|
|
|
|
|
if (SaveQuestionsToFile(questions)) {
|
|
|
|
|
std::cout << "题目生成完成,已保存到文件" << std::endl;
|
|
|
|
|
} else {
|
|
|
|
|
std::cerr << "错误:题目保存失败" << std::endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 按类型生成题目(文档附表2:小学/初中/高中难度差异)
|
|
|
|
|
std::string GenerateQuestionByType(const std::string& user_type) {
|
|
|
|
|
if (user_type == "小学") return GeneratePrimaryQuestion();
|
|
|
|
|
if (user_type == "初中") return GenerateJuniorQuestion();
|
|
|
|
|
return GenerateSeniorQuestion(); // 高中
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 小学题目(文档附表2:仅+,-,*,/和(),操作数1-100)
|
|
|
|
|
std::string GeneratePrimaryQuestion() {
|
|
|
|
|
const int operand_count = GenerateRandomInt(1, 5); // 1-5个操作数(文档需求6)
|
|
|
|
|
std::vector<int> operands;
|
|
|
|
|
for (int i = 0; i < operand_count; i++) {
|
|
|
|
|
operands.push_back(GenerateRandomInt(1, 100)); // 操作数范围1-100(文档需求6)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::vector<std::string> kOps = {"+", "-", "*", "/"};
|
|
|
|
|
std::vector<std::string> selected_ops;
|
|
|
|
|
for (int i = 0; i < operand_count - 1; i++) {
|
|
|
|
|
selected_ops.push_back(kOps[GenerateRandomInt(0, 3)]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 构建题目(50%概率加括号,文档附表2)
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
if (operand_count > 2 && GenerateRandomInt(0, 1)) {
|
|
|
|
|
const int paren_start = GenerateRandomInt(0, operand_count - 3);
|
|
|
|
|
ss << operands[0];
|
|
|
|
|
for (int i = 1; i < operand_count; i++) {
|
|
|
|
|
if (i == paren_start + 1) ss << selected_ops[i-1] << "(" << operands[i];
|
|
|
|
|
else if (i == paren_start + 2) ss << selected_ops[i-1] << operands[i] << ")";
|
|
|
|
|
else ss << selected_ops[i-1] << operands[i];
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ss << operands[0];
|
|
|
|
|
for (int i = 1; i < operand_count; i++) {
|
|
|
|
|
ss << selected_ops[i-1] << operands[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ss << " = ?";
|
|
|
|
|
return ss.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 初中题目(文档附表2:含平方/开根号,至少1个特殊运算符)
|
|
|
|
|
std::string GenerateJuniorQuestion() {
|
|
|
|
|
std::string question;
|
|
|
|
|
while (true) {
|
|
|
|
|
const int operand_count = GenerateRandomInt(1, 5);
|
|
|
|
|
std::vector<int> operands;
|
|
|
|
|
for (int i = 0; i < operand_count; i++) {
|
|
|
|
|
operands.push_back(GenerateRandomInt(1, 100));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::vector<std::string> kOps = {"+", "-", "*", "/", "^2", "√"};
|
|
|
|
|
bool has_special = false; // 标记是否含平方/开根号
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
|
|
|
|
|
// 处理第一个操作数(可能带特殊运算符)
|
|
|
|
|
const int first_op = GenerateRandomInt(0, 2);
|
|
|
|
|
if (first_op == 1) { ss << operands[0] << "^2"; has_special = true; }
|
|
|
|
|
else if (first_op == 2) { ss << "√" << operands[0]; has_special = true; }
|
|
|
|
|
else ss << operands[0];
|
|
|
|
|
|
|
|
|
|
// 处理后续操作数
|
|
|
|
|
for (int i = 1; i < operand_count; i++) {
|
|
|
|
|
const std::string op = kOps[GenerateRandomInt(0, 5)];
|
|
|
|
|
if (op == "^2" || op == "√") has_special = true;
|
|
|
|
|
ss << op;
|
|
|
|
|
if (op == "√") ss << operands[i];
|
|
|
|
|
else { ss << operands[i]; if (op == "^2") ss << "^2"; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ss << " = ?";
|
|
|
|
|
question = ss.str();
|
|
|
|
|
if (has_special) break; // 确保含特殊运算符(文档附表2)
|
|
|
|
|
}
|
|
|
|
|
return question;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 高中题目(文档附表2:含sin/cos/tan,至少1个三角函数)
|
|
|
|
|
std::string GenerateSeniorQuestion() {
|
|
|
|
|
std::string question;
|
|
|
|
|
while (true) {
|
|
|
|
|
const int operand_count = GenerateRandomInt(1, 5);
|
|
|
|
|
std::vector<int> operands;
|
|
|
|
|
for (int i = 0; i < operand_count; i++) {
|
|
|
|
|
operands.push_back(GenerateRandomInt(0, 180)); // 角度0-180°
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::vector<std::string> kOps = {"+", "-", "*", "/", "sin", "cos", "tan"};
|
|
|
|
|
bool has_trig = false; // 标记是否含三角函数
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
|
|
|
|
|
// 第一个操作数必为三角函数(确保至少一个,文档附表2)
|
|
|
|
|
const std::string trig_op = kOps[GenerateRandomInt(4, 6)];
|
|
|
|
|
ss << trig_op << "(" << operands[0] << "°)";
|
|
|
|
|
has_trig = true;
|
|
|
|
|
|
|
|
|
|
// 处理后续操作数
|
|
|
|
|
for (int i = 1; i < operand_count; i++) {
|
|
|
|
|
const std::string op = kOps[GenerateRandomInt(0, 6)];
|
|
|
|
|
if (op == "sin" || op == "cos" || op == "tan") {
|
|
|
|
|
ss << op << "(" << operands[i] << "°)";
|
|
|
|
|
} else {
|
|
|
|
|
ss << op << operands[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ss << " = ?";
|
|
|
|
|
question = ss.str();
|
|
|
|
|
if (has_trig) break; // 确保含三角函数(文档附表2)
|
|
|
|
|
}
|
|
|
|
|
return question;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 题目去重(文档需求3:基于用户历史记录)
|
|
|
|
|
bool IsQuestionDuplicate(const std::string& question) const {
|
|
|
|
|
const auto it = question_history_.find(current_user_.username);
|
|
|
|
|
return it != question_history_.end() && it->second.count(question);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加题目到历史记录(文档需求3)
|
|
|
|
|
void AddQuestionToHistory(const std::string& question) {
|
|
|
|
|
question_history_[current_user_.username].insert(question);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 加载历史题目(文档需求3:从账号文件夹读取已有文件)
|
|
|
|
|
void LoadQuestionHistory() {
|
|
|
|
|
const std::string user_dir = GetUserDirectory();
|
|
|
|
|
if (!std::filesystem::exists(user_dir)) return;
|
|
|
|
|
|
|
|
|
|
// 遍历账号文件夹下所有txt文件(文档需求5)
|
|
|
|
|
for (const auto& entry : std::filesystem::directory_iterator(user_dir)) {
|
|
|
|
|
if (entry.is_regular_file() && entry.path().extension() == ".txt") {
|
|
|
|
|
LoadQuestionsFromFile(entry.path().string());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 从文件加载题目到历史记录(文档需求3)
|
|
|
|
|
void LoadQuestionsFromFile(const std::string& file_path) {
|
|
|
|
|
std::ifstream file(file_path);
|
|
|
|
|
if (!file.is_open()) {
|
|
|
|
|
std::cerr << "警告:无法读取历史文件" << file_path << std::endl;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string line;
|
|
|
|
|
while (std::getline(file, line)) {
|
|
|
|
|
if (line.empty()) continue; // 跳过空行(文档需求5)
|
|
|
|
|
const size_t dot_pos = line.find('.');
|
|
|
|
|
if (dot_pos != std::string::npos) {
|
|
|
|
|
// 提取题目内容(去除题号)
|
|
|
|
|
std::string q = line.substr(dot_pos + 1);
|
|
|
|
|
q.erase(0, q.find_first_not_of(" \t"));
|
|
|
|
|
q.erase(q.find_last_not_of(" \t") + 1);
|
|
|
|
|
if (!q.empty()) AddQuestionToHistory(q);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
file.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 保存题目到文件(核心修复:解决中文路径编码与错误码判断)
|
|
|
|
|
bool SaveQuestionsToFile(const std::vector<std::string>& questions) {
|
|
|
|
|
const std::string user_dir = GetUserDirectory();
|
|
|
|
|
// 确保用户文件夹存在(文档需求5)
|
|
|
|
|
if (!std::filesystem::exists(user_dir)) {
|
|
|
|
|
std::cerr << "错误:用户文件夹" << user_dir << "不存在" << std::endl;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成合规文件名(文档需求5:年-月-日-时-分-秒.txt)
|
|
|
|
|
const std::string timestamp = GetCurrentTimestamp();
|
|
|
|
|
const std::string rel_path = user_dir + timestamp + ".txt";
|
|
|
|
|
// 转为Windows宽字符路径(修复中文编码问题)
|
|
|
|
|
std::wstring wide_path = Utf8ToWide(rel_path);
|
|
|
|
|
|
|
|
|
|
// 使用Windows API创建文件(避免C++标准库中文路径问题)
|
|
|
|
|
HANDLE file_handle = CreateFileW(
|
|
|
|
|
wide_path.c_str(),
|
|
|
|
|
GENERIC_WRITE,
|
|
|
|
|
0,
|
|
|
|
|
nullptr,
|
|
|
|
|
CREATE_ALWAYS,
|
|
|
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
|
|
|
nullptr
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (file_handle == INVALID_HANDLE_VALUE) {
|
|
|
|
|
std::cerr << "错误:无法创建文件" << rel_path
|
|
|
|
|
<< ",系统错误码:" << GetLastError() << std::endl;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 按文档需求5写入题目(带题号、每题空一行)
|
|
|
|
|
std::string content;
|
|
|
|
|
for (size_t i = 0; i < questions.size(); i++) {
|
|
|
|
|
content += std::to_string(i + 1) + ". " + questions[i] + "\r\n";
|
|
|
|
|
if (i < questions.size() - 1) content += "\r\n"; // 题目间空一行
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 写入文件(UTF-8编码,避免中文乱码)
|
|
|
|
|
DWORD bytes_written = 0;
|
|
|
|
|
bool write_ok = WriteFile(
|
|
|
|
|
file_handle,
|
|
|
|
|
content.c_str(),
|
|
|
|
|
static_cast<DWORD>(content.size()),
|
|
|
|
|
&bytes_written,
|
|
|
|
|
nullptr
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!write_ok || bytes_written != content.size()) {
|
|
|
|
|
std::cerr << "错误:文件写入失败,系统错误码:" << GetLastError() << std::endl;
|
|
|
|
|
CloseHandle(file_handle);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CloseHandle(file_handle);
|
|
|
|
|
std::cout << "文件保存成功,路径:" << rel_path << std::endl;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取用户文件夹路径(文档需求5:用户名/)
|
|
|
|
|
std::string GetUserDirectory() const {
|
|
|
|
|
return current_user_.username + "/";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 【核心修复】创建用户文件夹:解决中文路径与错误码判断
|
|
|
|
|
bool CreateUserDirectory(const std::string& dir_path) const {
|
|
|
|
|
// 转为Windows宽字符路径(修复中文编码问题)
|
|
|
|
|
std::wstring wide_dir = Utf8ToWide(dir_path);
|
|
|
|
|
|
|
|
|
|
// 使用Windows API创建目录(避免C++标准库中文问题)
|
|
|
|
|
if (CreateDirectoryW(wide_dir.c_str(), nullptr)) {
|
|
|
|
|
std::cout << "成功:创建用户文件夹" << dir_path << std::endl;
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
DWORD err_code = GetLastError();
|
|
|
|
|
// 错误码183:目录已存在(视为成功,文档允许复用文件夹)
|
|
|
|
|
if (err_code == ERROR_ALREADY_EXISTS) {
|
|
|
|
|
std::cout << "提示:用户文件夹" << dir_path << "已存在" << std::endl;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// 其他错误(如权限不足)
|
|
|
|
|
std::cerr << "错误:创建文件夹" << dir_path << "失败,系统错误码:" << err_code << std::endl;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 【辅助修复】UTF-8转Windows宽字符(解决中文路径编码)
|
|
|
|
|
std::wstring Utf8ToWide(const std::string& utf8_str) const {
|
|
|
|
|
int wide_len = MultiByteToWideChar(
|
|
|
|
|
CP_UTF8,
|
|
|
|
|
0,
|
|
|
|
|
utf8_str.c_str(),
|
|
|
|
|
-1,
|
|
|
|
|
nullptr,
|
|
|
|
|
0
|
|
|
|
|
);
|
|
|
|
|
if (wide_len <= 0) return L"";
|
|
|
|
|
|
|
|
|
|
std::wstring wide_str(wide_len, 0);
|
|
|
|
|
MultiByteToWideChar(
|
|
|
|
|
CP_UTF8,
|
|
|
|
|
0,
|
|
|
|
|
utf8_str.c_str(),
|
|
|
|
|
-1,
|
|
|
|
|
&wide_str[0],
|
|
|
|
|
wide_len
|
|
|
|
|
);
|
|
|
|
|
return wide_str;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取时间戳(文档需求5:年-月-日-时-分-秒,无非法字符)
|
|
|
|
|
std::string GetCurrentTimestamp() const {
|
|
|
|
|
const auto now = std::time(nullptr);
|
|
|
|
|
const auto local = *std::localtime(&now);
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << std::put_time(&local, "%Y-%m-%d-%H-%M-%S"); // 无冒号,符合Windows命名
|
|
|
|
|
return ss.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 退出登录(文档需求2)
|
|
|
|
|
void Logout() {
|
|
|
|
|
is_logged_in_ = false;
|
|
|
|
|
current_user_ = UserAccount();
|
|
|
|
|
std::cout << "已退出当前用户,请重新登录" << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成随机整数
|
|
|
|
|
int GenerateRandomInt(int min, int max) {
|
|
|
|
|
std::uniform_int_distribution<int> dist(min, max);
|
|
|
|
|
return dist(rng_);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace math_test_generator
|
|
|
|
|
|
|
|
|
|
// 程序入口(设置控制台UTF-8编码,解决中文显示)
|
|
|
|
|
int main() {
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
SetConsoleOutputCP(CP_UTF8);
|
|
|
|
|
SetConsoleCP(CP_UTF8);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
math_test_generator::MathTestGenerator generator;
|
|
|
|
|
generator.Run();
|
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
|
std::cerr << "程序异常退出:" << e.what() << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|