中小学数学卷子自动生成程序

本来是不打算写这个课程作业的博客的,但是后续结队编程又需要和队友相互交流代码,而我当时写代码的时候也没有过多的注释,为了我的结队队友“、、”能够更轻松的(至少不会想捶我)完成课程任务,我还是准备写篇博客简单介绍一下课程项目。
PS:也附上“、、”的博客网站,也是关于这次的项目的

程序要求

点击下载需求文件

程序架构

使用java实现程序,总有5个类,并使用数据库来查重

1.Main

程序运行的地方,主要处理逻辑,程序状态和用户交互之类的

2.User抽象类

代表出题者的一个抽象类,拥有登录(连接数据库),出题,检查题目是否重复的功能

3.Primary,JuniorHigh,High三个实例用户类

继承User,并实现出题的具体方法

4.mysql数据库

实现题目的查重功能和用户信息存储功能


下面将从Main类开始介绍整个程序

Main类

变量

private int state = LOGIN;//代表程序状态,初始状态为登录状态
private static final int LOGIN = 0;//定义宏LOGIN
private static final int LOGGED_IN = 1;//定义宏LOGGED_IN
private User user;//当前程序的使用用户
private static Scanner scanner = new Scanner(System.in);//实例化输入对象

main方法,程序入口

public static void main(String[] args) throws SQLException, ClassNotFoundException {
    System.out.println("中小学数学卷子自动生成程序");
    Main main = new Main();
    while (true) {
        //根据程序状态实现不同功能
        switch (main.state) {
            case LOGIN:
                //实现登录功能
                main.processLogin();
                break;
            case LOGGED_IN:
                //实现登录后功能
                main.processLoggedIn();
                break;
        }
    }
}

processLogin()

private void processLogin() throws SQLException, ClassNotFoundException {
    System.out.println("请输入用户名、密码");
    String account = scanner.next();
    String password = scanner.next();
    //根据输入的账号密码获取用户实例对象
    user = User.login(account, password);
    if (user == null) {
        System.out.println("请输入正确的用户名、密码");
    } else {
        System.out.println("当前选择为" + user.getDifficultyType() + "出题");
        //将状态更新为已登录
        state = LOGGED_IN;
    }
}

processLoggedIn()

private void processLoggedIn() {
    System.out.println("准备生成" + user.getDifficultyType() + "数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):");
    //接收用户输入
    String scannerContent = scanner.next();
    //监测用户输入是否为int型数据
    try {
        int questionsNumber = Integer.parseInt(scannerContent);
        //-1为退出登录,将状态重置为登录状态,并将用户对象置为null
        if (questionsNumber == -1) {
            state = LOGIN;
            user = null;
        } else if (questionsNumber >= 10 && questionsNumber <= 30) {
            System.out.println("指令正确");
            //开始生产题目,传入的参数为:用户的难度类型,题目数量
            user.generateQuestionsByType(user.getDifficultyType(), questionsNumber);
        } else {
            System.out.println("请输入正确的指令");
        }
    }
    //用户输入为非int型,监测是否为“切换为”指令
    catch (Exception NumberFormatException) {
        if (scannerContent.startsWith("切换为")) {
            String type = scannerContent.substring(3);
            //重置用户的难度类型
            if (type.equals("小学") || type.equals("初中") || type.equals("高中")) {
                user.setDifficultyType(type);
            } else {
                System.out.println("请输入正确的指令");
            }
        } else {
            System.out.println("请输入正确的指令");
        }
    }
}

User类

变量

    private String type;//用户类型
    private String account;//用户账号
    private String password;//用户密码
    private String difficultyType;//难度类型
    //下面的变量需要设置为自己的数据库数据
    private static final String URL = "jdbc:mysql://localhost:3306/testgeneration?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";//数据库连接地址
    private static final String USER = "root";//数据库用户名
    private static final String PASSWORD = "1234";//数据库密码

构造器,getter,setter不再赘述

但是需要注意保护用户隐私,不比设置密码的getter和setter

login

public static User login(String account, String password) throws SQLException, ClassNotFoundException {
    //1.加载驱动程序
    Class.forName("com.mysql.cj.jdbc.Driver");
    String searchedType;//从数据库获取到的类型
    String sql = "SELECT identity from user where account=? and password=?";
    //数据库的运行很耗费资源,所以需要及时关闭
    try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD)) {
        //下面使用的是PreparedStatement,用法会贴在下面
        try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
            preparedStatement.setObject(1, account);
            preparedStatement.setObject(2, password);
            try (ResultSet resultSet = preparedStatement.executeQuery()) {
                if (resultSet.next()) {
                    //从数据库获取类型
                    searchedType = resultSet.getString("identity");
                    //返回实例对象
                    switch (searchedType) {
                        case "小学":
                            return new Primary(account, password, searchedType);
                        case "初中":
                            return new JuniorHigh(account, password, searchedType);
                        case "高中":
                            return new High(account, password, searchedType);
                    }
                }
                //查询失败,返回null
                else {
                    return null;
                }
            }
        }
    }
    return null;
}

statement 、prepareStatement的用法和解释,转自CSDN

getQuestionsArray

//由子类实现的抽象方法
public abstract String[] getQuestionsArray(int number);

generateQuestions

//根据输入的题目数量生成题目,并将其添加到数据库
public void generateQuestions(int number) {
    Timestamp timestamp = new Timestamp(System.currentTimeMillis());
    try {
        //测试重复问题,addQuestionsToDatabase会返回添加失败的个数,getQuestionsArray是根据不同类型的对象生成的,也就是不同难度的题目生成方法
        int failNum = addQuestionsToDatabase(getQuestionsArray(number), timestamp);
        while (failNum != 0) {
            failNum = addQuestionsToDatabase(getQuestionsArray(failNum), timestamp);
        }
    } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    }
}

addQuestionsToDatabase

//返回添加失败的问题的个数
protected int addQuestionsToDatabase(String[] questions, Timestamp time) throws ClassNotFoundException, SQLException {
    int count = 0;
    //1.加载驱动程序
    Class.forName("com.mysql.cj.jdbc.Driver");
    String sql = "INSERT INTO testgeneration.questions (question, account, created_time) VALUES (?, ?, ?);";
    try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD)) {
        try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
            //创建文件对象
            File file = new File("questionsData/" + account);
            //如果没有用户的文件就创建文件夹
            if (!file.exists()) {
                file.mkdir();
            }
//                System.out.println(file.getPath());
            //创建文件写入对象
            try (FileWriter fileWriter = new FileWriter(file.getPath() + "/" + new SimpleDateFormat("yyyy年-MM月-dd日-HH时-mm分-ss秒").format(time) + ".txt", true)) {
                //在多次写入时preparedStatement就能大大降低运行成本
                for (int i = 0; i < questions.length; i++) {
                    preparedStatement.setObject(1, questions[i]);
                    preparedStatement.setObject(2, account);
                    preparedStatement.setObject(3, time);
                    try {
                        preparedStatement.executeUpdate();
                        fileWriter.write(questions[i] + "\r\n");
                    }
                    //如果重复则会报错,并将重复的题目数量+1
                    catch (SQLException e) {
                        System.out.println("'" + questions[i] + "'" + "与之前的题目重复,准备重新出题");
                        count++;
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
    return count;
}

User子类

User的子类主要介绍生成题目的方法

Primary

//符号集
private static final String[] symbols = {"+", "-", "*", "/"};
@Override
public String[] getQuestionsArray(int number) {
    String[] questions = new String[number];
    //StringBuilder在操作变换的字符串的时候效率更高
    StringBuilder question = new StringBuilder();
    //以当前时间作为随机种子
    Random random = new Random(System.currentTimeMillis());
    for (int index = 0; index < number; index++) {
        //随机生成操作数数量,最少为两个
        int operandsNumber = random.nextInt(4) + 2;
        //随机生成左括号的位置
        int bracketStart = random.nextInt(operandsNumber);
        //随机生成右括号的位置
        int bracketEnd = random.nextInt(operandsNumber);
        //判断括号位置是否合法
        boolean isBracketIllegal = bracketStart < bracketEnd;
        int[] operands = new int[operandsNumber];
        for (int i = 0; i < operandsNumber; i++) {
            //操作数为1~100
            operands[i] = random.nextInt(100) + 1;
            //括号位置合法,并且当前为左括号位置
            if (isBracketIllegal && i == bracketStart) {
                question.append("(");
            }
            question.append(operands[i]);
            //操作数合法并且当前为右括号位置
            if (isBracketIllegal && i == bracketEnd) {
                question.append(")");
            }
            if (i < operandsNumber - 1) {
                //随机从符号集里抽取一个符号
                int symbolIndex = random.nextInt(symbols.length);
                question.append(symbols[symbolIndex]);
            }
            //最后一个操作数后面为=号
            else {
                question.append("=");
            }
        }
        questions[index] = question.toString();
        System.out.println(question);
        //重新设置问题
        question.delete(0, question.length());
    }
    return questions;
}

JuniorHigh

//符号集
private static final String[] symbols = {"+", "-", "*", "/"};
@Override
public String[] getQuestionsArray(int number) {
    String[] questions = new String[number];
    StringBuilder question = new StringBuilder();
    Random random = new Random(System.currentTimeMillis());
    for (int index = 0; index < number; index++) {
        //操作数数量,1到5个
        int operandsNumber = random.nextInt(5) + 1;
        //括号起始点
        int bracketStart = random.nextInt(operandsNumber);
        //括号终止点
        int bracketEnd = random.nextInt(operandsNumber);
        //初中符号个数
        int juniorSymbolNum = operandsNumber == 1 ? 1 : random.nextInt(operandsNumber) + 1;
        //根号个数
        int RadicalNum = random.nextInt(juniorSymbolNum + 1);
        //平方个数
        int squareNum = juniorSymbolNum - RadicalNum;
        //是否有括号
        boolean isBracketIllegal = bracketStart < bracketEnd;
        int[] operands = new int[operandsNumber];
        for (int i = 0; i < operandsNumber; i++) {
            //是否加根号
            boolean addRadical = operandsNumber == 1 ? RadicalNum == 1 : random.nextBoolean();
            //是否加平方
            boolean addSquare = operandsNumber == 1 ? !addRadical : !addRadical && random.nextBoolean();
            operands[i] = random.nextInt(100) + 1;
            if (isBracketIllegal && i == bracketStart) {
                //在括号外加根号
                boolean outsideRadical = random.nextBoolean();
                if (outsideRadical && RadicalNum > 0) {
                    question.append("√");
                    RadicalNum--;
                }
                question.append("(");
            }
            //普通根号
            if (addRadical && RadicalNum > 0) {
                question.append("√");
                RadicalNum--;
            }
            question.append(operands[i]);
            //普通平方
            if (addSquare && squareNum > 0) {
                question.append("²");
                squareNum--;
            }
            if (isBracketIllegal && i == bracketEnd) {
                boolean outsideSquare = random.nextBoolean();

                question.append(")");
                //括号外平方
                if (outsideSquare && squareNum > 0) {
                    question.append("²");
                    squareNum--;
                }
            }
            if (i < operandsNumber - 1) {
                int symbolIndex = random.nextInt(symbols.length);
                question.append(symbols[symbolIndex]);
            }
            //最后一个操作数后面为=号
            else {
                question.append("=");
            }
        }
        questions[index] = question.toString();
        System.out.println(question);
        question.delete(0, question.length());
    }
    return questions;
}

High

//符号集
private static final String[] symbols = {"+", "-", "*", "/"};
private static final String[] highSymbols = {"sin", "cos", "tan"};
@Override
public String[] getQuestionsArray(int number) {
    String[] questions = new String[number];
    StringBuilder question = new StringBuilder();
    Random random = new Random(System.currentTimeMillis());
    for (int index = 0; index < number; index++) {
        //操作数数量,1到5个
        int operandsNumber = random.nextInt(5) + 1;
        //括号起始点
        int bracketStart = random.nextInt(operandsNumber);
        //括号终止点
        int bracketEnd = random.nextInt(operandsNumber);
        //初中符号个数
        int juniorSymbolNum = random.nextInt(operandsNumber) + 1;
        //根号个数
        int RadicalNum = random.nextInt(juniorSymbolNum + 1);
        //平方个数
        int squareNum = juniorSymbolNum - RadicalNum;
        //高中符号个数
        int highSymbolNum = operandsNumber == 1 ? 1 : random.nextInt(operandsNumber) + 1;

        //是否有括号
        boolean isBracketIllegal = bracketStart < bracketEnd;
        int[] operands = new int[operandsNumber];
        for (int i = 0; i < operandsNumber; i++) {

            //是否加高中符号
            boolean addHighSymbol = operandsNumber == 1 || random.nextBoolean();
            //是否加根号
            boolean addRadical = !addHighSymbol && random.nextBoolean();
            //是否加平方
            boolean addSquare = !addRadical && random.nextBoolean();
            operands[i] = random.nextInt(100) + 1;
            if (isBracketIllegal && i == bracketStart) {
                //在括号外加根号
                boolean outsideRadical = random.nextBoolean();
                //在括号外加高中符号
                boolean outsideHighSymbol = !outsideRadical && random.nextBoolean();
                if (outsideRadical && RadicalNum > 0) {
                    question.append("√");
                    RadicalNum--;
                }
                if (outsideHighSymbol && highSymbolNum > 0) {
                    question.append(highSymbols[random.nextInt(highSymbols.length)]);
                    highSymbolNum--;
                }
                question.append("(");
            }
            //普通根号
            if (addRadical && RadicalNum > 0) {
                question.append("√");
                RadicalNum--;
            }
            if (addHighSymbol && highSymbolNum > 0) {
                question.append(highSymbols[random.nextInt(highSymbols.length)]);
                highSymbolNum--;
            }
            question.append(operands[i]);
            //普通平方
            if (addSquare && squareNum > 0) {
                question.append("²");
                squareNum--;
            }
            if (isBracketIllegal && i == bracketEnd) {
                boolean outsideSquare = random.nextBoolean();

                question.append(")");
                //括号外平方
                if (outsideSquare && squareNum > 0) {
                    question.append("²");
                    squareNum--;
                }
            }
            if (i < operandsNumber - 1) {
                int symbolIndex = random.nextInt(symbols.length);
                question.append(symbols[symbolIndex]);
            }
            //最后一个操作数后面为=号
            else {
                question.append("=");
            }
        }
        questions[index] = question.toString();
        System.out.println(question);
        question.delete(0, question.length());
    }
    return questions;
}

数据库

user用户信息

create table user
(
    account  varchar(12) not null
        primary key,
    password varchar(15) null,
    identity varchar(5)  null
);

questions题目信息

将question和account设置为键,使添加时,同一个用户不能重发添加一个题,实现题目查重

create table questions
(
    q_id         int auto_increment
        primary key,
    question     varchar(30) null,
    account      varchar(8)  null,
    created_time timestamp   null,
    constraint questions_pk
        unique (question, account),
    constraint questions_fk
        foreign key (account) references user (account)
);

完整程序代码

项目完整文件已经上传到github,仅供参考

更多

欢迎来访我的个人网站

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值