Compare commits

...

2 Commits

@ -0,0 +1,33 @@
# 中小学数学卷子自动生成程序 - 使用说明
## 1. 项目功能
- **登录验证**:预设小学/初中/高中各3个账号密码统一为123。
- **题目生成**:按学段生成符合难度的题目(小学+-*/()、初中含平方/根号、高中含三角函数)。
- **学段切换**登录后输入“切换为XX”如“切换为初中”可切换出题类型。
- **题目查重**:同一老师生成的题目不与历史题目重复。
- **文件保存**:题目按“年-月-日-时-分-秒.txt”保存到用户专属文件夹。
## 2. 预设账号
| 学段 | 用户名 | 密码 |
|--------|------|------|
| 小学 | 张三 1 | 123 |
| 小学 | 张三 2 | 123 |
| 小学 | 张三 3 | 123 |
| 初中 | 李四 1 | 123 |
| 初中 | 李四 2 | 123 |
| 初中 | 李四 3 | 123 |
| 高中 | 王五 1 | 123 |
| 高中 | 王五 2 | 123 |
| 高中 | 王五 3 | 123 |
## 3. 使用步骤
1. **运行程序**:执行`com.mathpaper.main.Main`类的`main`方法。
2. **登录**:输入“用户名 密码”(如“张三 1 123”登录成功后显示当前学段。
3. **生成题目**输入10-30的数字程序自动生成并保存题目。
4. **切换学段**:输入“切换为初中”(示例),切换后可生成对应学段题目。
5. **退出登录**:输入-1返回登录界面。
## 4. 注意事项
- 题目数量仅支持10-30输入其他数字除-1外会提示重试。
- 生成的题目保存在项目根目录`generated_papers`下的用户文件夹中,请勿手动删除。
- 切换学段时需严格按照“切换为小学/初中/高中”格式输入,否则提示无效。

@ -0,0 +1,49 @@
package com.mathpaper.file;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
public class FileHandler {
// 基础目录项目根目录下的“generated_papers”相对路径符合需求
public static final String BASE_DIR = "generated_papers";
// 创建用户专属文件夹(若不存在)
public String createUserDir(String username) {
String userDirPath = BASE_DIR + File.separator + username;
File userDir = new File(userDirPath);
if (!userDir.exists()) {
if (userDir.mkdirs()) {
System.out.println("用户文件夹创建成功:" + userDirPath);
} else {
System.out.println("用户文件夹创建失败!");
}
}
return userDirPath;
}
// 保存题目到文件(格式:年-月-日-时-分-秒.txt
public void saveQuestions(String username, List<String> questions) {
String userDirPath = createUserDir(username);
// 生成文件名(符合需求格式)
String fileName = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()) + ".txt";
String filePath = userDirPath + File.separator + fileName;
// 写入题目(每题空一行)
try (FileWriter writer = new FileWriter(filePath, StandardCharsets.UTF_8)) {
for (int i = 0; i < questions.size(); i++) {
writer.write(questions.get(i));
if (i != questions.size() - 1) {
writer.write("\n\n"); // 每题之间空一行
}
}
System.out.println("题目保存成功!文件路径:" + filePath);
} catch (IOException e) {
System.out.println("保存题目失败:" + e.getMessage());
}
}
}

@ -0,0 +1,128 @@
package com.mathpaper.main;
import com.mathpaper.user.AccountManager;
import com.mathpaper.question.QuestionGenerator;
import com.mathpaper.question.PrimaryQuestionGenerator;
import com.mathpaper.question.JuniorQuestionGenerator;
import com.mathpaper.question.SeniorQuestionGenerator;
import com.mathpaper.file.FileHandler;
import java.util.List;
import java.util.Scanner;
public class Main {
private static final Scanner SCANNER = new Scanner(System.in);
private static final FileHandler FILE_HANDLER = new FileHandler();
public static void main(String[] args) {
System.out.println("=== 中小学数学卷子自动生成程序 ===");
while (true) {
// 1. 登录流程(完全不变)
String username = login();
if (username == null) continue;
// 2. 登录后操作:初始学段为账号对应的学段(完全不变)
String currentType = AccountManager.getAccountType(username);
while (true) {
// 提示信息:始终使用当前有效的 currentType无效切换后不变
System.out.print("\n准备生成" + currentType + "数学题目请输入生成题目数量10-30输入-1退出当前用户");
String input = SCANNER.nextLine().trim();
// 处理学段切换:传入当前学段,确保无效时保留原学段
if (input.startsWith("切换为")) {
// 关键修改:接收方法返回的学段(有效则更新,无效则保持原值)
currentType = handleTypeSwitch(input, currentType);
continue;
}
// 处理题目数量输入(完全不变)
int count = parseCount(input);
if (count == -1) {
System.out.println("退出当前用户,返回登录界面...");
break;
}
if (count == -2) {
System.out.println("输入无效,请重新输入!");
continue;
}
// 3. 生成并保存题目
QuestionGenerator generator = getGenerator(currentType);
List<String> questions = generator.generate(count, username);
FILE_HANDLER.saveQuestions(username, questions);
// 4. 继续操作提示
System.out.print("是否继续生成" + currentType + "题目?(输入“是”继续,其他返回主页):");
if (!SCANNER.nextLine().trim().equals("是")) {
System.out.println("返回主页,可重新输入数量或切换学段...");
}
}
}
}
// 登录逻辑
private static String login() {
while (true) {
System.out.print("请输入用户名和密码(空格隔开):");
String input = SCANNER.nextLine().trim();
String[] parts = input.split(" ");
if (parts.length < 2) {
System.out.println("输入格式错误示例元歌1 123");
continue;
}
String username = input.substring(0, input.lastIndexOf(" "));
String password = input.substring(input.lastIndexOf(" ") + 1);
if (AccountManager.validate(username, password)) {
System.out.println("登录成功!当前选择为" + AccountManager.getAccountType(username) + "出题");
return username;
} else {
System.out.println("请输入正确的用户名、密码");
}
}
}
/**
* currentType
*
*/
private static String handleTypeSwitch(String input, String currentType) {
String targetType = input.substring(3).trim();
// 有效学段:正常切换并返回新学段
if (targetType.equals("小学") || targetType.equals("初中") || targetType.equals("高中")) {
System.out.println("成功切换为" + targetType);
return targetType;
}
// 无效学段:仅提示错误,返回原学段(不改变 currentType
else {
System.out.println("请输入小学、初中和高中三个选项中的一个");
return currentType; // 核心:返回修改前的学段,保持不变
}
}
// 解析题目数量(完全不变)
private static int parseCount(String input) {
try {
int count = Integer.parseInt(input);
if (count == -1) return -1;
if (count >= 10 && count <= 30) return count;
System.out.println("题目数量需在10-30之间");
return -2;
} catch (NumberFormatException e) {
return -2;
}
}
// 获取出题器(完全不变)
private static QuestionGenerator getGenerator(String type) {
switch (type) {
case "小学": return new PrimaryQuestionGenerator();
case "初中": return new JuniorQuestionGenerator();
case "高中": return new SeniorQuestionGenerator();
default:
// 兜底:避免极端情况崩溃,默认生成小学题
System.out.println("学段异常,默认生成小学数学题目");
return new PrimaryQuestionGenerator();
}
}
}

@ -0,0 +1,52 @@
package com.mathpaper.question;
import com.mathpaper.util.Utils;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class JuniorQuestionGenerator implements QuestionGenerator {
private final QuestionChecker checker = new QuestionChecker();
private final Random RANDOM = new Random();
@Override
public List<String> generate(int count, String username) {
List<String> questions = new ArrayList<>();
while (questions.size() < count) {
String expr = generateSingle();
if (!checker.isDuplicate(username, expr)) {
questions.add((questions.size() + 1) + ". " + expr + " = ");
}
}
return questions;
}
// 生成一道初中题(确保含平方/开根号)
private String generateSingle() {
String expr = "";
do {
int operandCount = Utils.getOperandCount();
StringBuilder exprBuilder = new StringBuilder();
for (int i = 0; i < operandCount; i++) {
int num = Utils.getRandomOperand();
String operand;
// 30%概率将操作数改为平方或开根号
if (RANDOM.nextDouble() < 0.3) {
operand = RANDOM.nextBoolean() ? Utils.getSquare(String.valueOf(num)) : Utils.getSqrt(String.valueOf(num));
} else {
operand = String.valueOf(num);
}
exprBuilder.append(operand);
if (i < operandCount - 1) {
exprBuilder.append(" ").append(Utils.getPrimaryOp()).append(" ");
}
}
expr = Utils.addBrackets(exprBuilder.toString());
} while (!Utils.isJuniorValid(expr)); // 校验难度
return expr;
}
}

@ -0,0 +1,40 @@
package com.mathpaper.question;
import com.mathpaper.util.Utils;
import java.util.ArrayList;
import java.util.List;
public class PrimaryQuestionGenerator implements QuestionGenerator {
private final QuestionChecker checker = new QuestionChecker();
@Override
public List<String> generate(int count, String username) {
List<String> questions = new ArrayList<>();
// 生成不重复题目
while (questions.size() < count) {
String expr = generateSingle();
if (!checker.isDuplicate(username, expr)) {
// 添加题号和等号如“1. 2 + 3 = ”)
questions.add((questions.size() + 1) + ". " + expr + " = ");
}
}
return questions;
}
// 生成一道小学题
private String generateSingle() {
int operandCount = Utils.getOperandCount();
StringBuilder expr = new StringBuilder();
// 拼接操作数和运算符
for (int i = 0; i < operandCount; i++) {
expr.append(Utils.getRandomOperand());
if (i < operandCount - 1) {
expr.append(" ").append(Utils.getPrimaryOp()).append(" ");
}
}
// 添加括号(可选)
return Utils.addBrackets(expr.toString());
}
}

@ -0,0 +1,66 @@
package com.mathpaper.question;
import com.mathpaper.file.FileHandler;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class QuestionChecker {
private static final String BASE_DIR = FileHandler.BASE_DIR;
// 检查题目是否重复(参数:用户名、新题目核心内容)
public boolean isDuplicate(String username, String newExpr) {
String userDirPath = BASE_DIR + File.separator + username;
File userDir = new File(userDirPath);
// 若用户文件夹不存在,无历史题目,不重复
if (!userDir.exists()) {
return false;
}
// 读取用户所有历史题目文件
File[] historyFiles = userDir.listFiles(f -> f.getName().endsWith(".txt"));
if (historyFiles == null || historyFiles.length == 0) {
return false;
}
// 收集所有历史题目核心内容
List<String> historyExprs = new ArrayList<>();
for (File file : historyFiles) {
historyExprs.addAll(readHistoryExprs(file));
}
// 对比新题目(忽略题号和等号)
String newCore = extractCoreExpr(newExpr);
for (String historyCore : historyExprs) {
if (historyCore.equals(newCore)) {
return true; // 重复
}
}
return false; // 不重复
}
// 从历史文件中读取题目核心内容
private List<String> readHistoryExprs(File file) {
List<String> exprs = new ArrayList<>();
try (Scanner scanner = new Scanner(file)) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
// 仅处理题目行格式“1. ... = ”)
if (line.matches("^\\d+\\. .+ = $")) {
exprs.add(extractCoreExpr(line.replaceFirst("^\\d+\\. ", "")));
}
}
} catch (FileNotFoundException e) {
System.out.println("读取历史文件失败:" + e.getMessage());
}
return exprs;
}
// 提取题目核心内容(去掉“ = ”)
private String extractCoreExpr(String expr) {
return expr.replace(" = ", "").trim();
}
}

@ -0,0 +1,8 @@
package com.mathpaper.question;
import java.util.List;
public interface QuestionGenerator {
// 生成count道不重复题目参数题目数量、当前用户名用于查重
List<String> generate(int count, String username);
}

@ -0,0 +1,54 @@
package com.mathpaper.question;
import com.mathpaper.util.Utils;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class SeniorQuestionGenerator implements QuestionGenerator {
private final QuestionChecker checker = new QuestionChecker();
private final Random RANDOM = new Random();
@Override
public List<String> generate(int count, String username) {
List<String> questions = new ArrayList<>();
while (questions.size() < count) {
String expr = generateSingle();
if (!checker.isDuplicate(username, expr)) {
questions.add((questions.size() + 1) + ". " + expr + " = ");
}
}
return questions;
}
// 生成一道高中题(确保含三角函数)
private String generateSingle() {
String expr;
do {
int operandCount = Utils.getOperandCount();
StringBuilder exprBuilder = new StringBuilder();
for (int i = 0; i < operandCount; i++) {
String operand;
// 40%概率生成三角函数30%平方/开根号30%普通数
double prob = RANDOM.nextDouble();
if (prob < 0.4) {
operand = Utils.getTrigFunc();
} else if (prob < 0.7) {
int num = Utils.getRandomOperand();
operand = RANDOM.nextBoolean() ? Utils.getSquare(String.valueOf(num)) : Utils.getSqrt(String.valueOf(num));
} else {
operand = String.valueOf(Utils.getRandomOperand());
}
exprBuilder.append(operand);
if (i < operandCount - 1) {
exprBuilder.append(" ").append(Utils.getPrimaryOp()).append(" ");
}
}
expr = Utils.addBrackets(exprBuilder.toString());
} while (!Utils.isSeniorValid(expr)); // 校验难度
return expr;
}
}

@ -0,0 +1,19 @@
package com.mathpaper.user;
public class Account {
private String username;
private String password;
private String type; // 小学/初中/高中
// 构造方法
public Account(String username, String password, String type) {
this.username = username;
this.password = password;
this.type = type;
}
// Getter方法无Setter确保账号信息不可篡改
public String getUsername() { return username; }
public String getPassword() { return password; }
public String getType() { return type; }
}

@ -0,0 +1,45 @@
package com.mathpaper.user;
import java.util.ArrayList;
import java.util.List;
public class AccountManager {
// 预设账号列表(静态初始化,程序启动时加载)
private static final List<Account> ACCOUNTS;
static {
ACCOUNTS = new ArrayList<>();
// 小学账号
ACCOUNTS.add(new Account("张三1", "123", "小学"));
ACCOUNTS.add(new Account("张三2", "123", "小学"));
ACCOUNTS.add(new Account("张三3", "123", "小学"));
// 初中账号
ACCOUNTS.add(new Account("李四1", "123", "初中"));
ACCOUNTS.add(new Account("李四2", "123", "初中"));
ACCOUNTS.add(new Account("李四3", "123", "初中"));
// 高中账号
ACCOUNTS.add(new Account("王五1", "123", "高中"));
ACCOUNTS.add(new Account("王五2", "123", "高中"));
ACCOUNTS.add(new Account("王五3", "123", "高中"));
}
// 验证用户名和密码是否正确
public static boolean validate(String username, String password) {
for (Account acc : ACCOUNTS) {
if (acc.getUsername().equals(username) && acc.getPassword().equals(password)) {
return true;
}
}
return false;
}
// 根据用户名获取学段(登录验证通过后调用)
public static String getAccountType(String username) {
for (Account acc : ACCOUNTS) {
if (acc.getUsername().equals(username)) {
return acc.getType();
}
}
return null; // 理论上不会触发(已验证账号)
}
}

@ -0,0 +1,118 @@
package com.mathpaper.util;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Utils {
private static final Random RANDOM = new Random();
private static final String[] PRIMARY_OPS = {"+", "-", "*", "/"};
private static final String[] TRIG_FUNCS = {"sin", "cos", "tan"};
private static final Pattern SUB_EXPR_PATTERN = Pattern.compile("\\d+\\s[+\\-*/]\\s\\d+");
// 生成1-100的随机操作数
public static int getRandomOperand() {
return RANDOM.nextInt(100) + 1;
}
// 生成2-5个操作数
public static int getOperandCount() {
return RANDOM.nextInt(4) + 2;
}
// 生成小学随机运算符
public static String getPrimaryOp() {
return PRIMARY_OPS[RANDOM.nextInt(PRIMARY_OPS.length)];
}
// 生成三角函数
public static String getTrigFunc() {
String func = TRIG_FUNCS[RANDOM.nextInt(TRIG_FUNCS.length)];
int angle = RANDOM.nextInt(181);
return func + "(" + angle + ")°";
}
// 生成平方
public static String getSquare(String num) {
return num + "\u00B2";
}
// 生成开根号
public static String getSqrt(String num) {
return "√(" + num + ")";
}
// 添加括号(拆分后主方法)
public static String addBrackets(String expr) {
if (!shouldAddBrackets(expr)) return expr;
Matcher matcher = SUB_EXPR_PATTERN.matcher(expr);
int[] positions = findValidSubExpression(matcher, expr);
return positions[0] == -1 ? expr : wrapSubExpression(expr, positions);
}
// 辅助:判断是否需要添加括号
private static boolean shouldAddBrackets(String expr) {
return expr.length() >= 5 && countOperands(expr) >= 3 && RANDOM.nextBoolean();
}
// 辅助:查找合法的子表达式位置
private static int[] findValidSubExpression(Matcher matcher, String expr) {
int[] positions = {-1, -1};
while (matcher.find()) {
if (matcher.start() > 0 || matcher.end() < expr.length() - 1) {
positions[0] = matcher.start();
positions[1] = matcher.end();
break;
}
}
return positions;
}
// 辅助:用括号包裹子表达式
private static String wrapSubExpression(String expr, int[] positions) {
return expr.substring(0, positions[0]) + "(" +
expr.substring(positions[0], positions[1]) + ")" +
expr.substring(positions[1]);
}
// 统计操作数数量
private static int countOperands(String expr) {
return expr.split("\\s[+\\-*/]\\s").length;
}
// 校验初中题目
public static boolean isJuniorValid(String expr) {
return expr.contains("\u00B2") || expr.contains("√");
}
// 校验高中题目
public static boolean isSeniorValid(String expr) {
return expr.contains("sin") || expr.contains("cos") || expr.contains("tan");
}
// 验证表达式合法性
public static boolean isValidExpression(String expr) {
return hasValidOperator(expr) && !hasConsecutiveOperators(expr) && hasMatchingBrackets(expr);
}
// 辅助:检查是否包含至少一个运算符
private static boolean hasValidOperator(String expr) {
return expr.matches(".*[+\\-*/].*");
}
// 辅助:检查是否有连续运算符
private static boolean hasConsecutiveOperators(String expr) {
return expr.matches(".*[+\\-*/]\\s*[+\\-*/].*");
}
// 辅助:检查括号是否匹配
private static boolean hasMatchingBrackets(String expr) {
int bracketCount = 0;
for (char c : expr.toCharArray()) {
if (c == '(') bracketCount++;
else if (c == ')') bracketCount--;
if (bracketCount < 0) return false;
}
return bracketCount == 0;
}
}
Loading…
Cancel
Save