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


    本来是不打算写这个课程作业的博客的,但是后续结队编程又需要和队友相互交流代码,而我当时写代码的时候也没有过多的注释,为了我的结队队友“、、”能够更轻松的(至少不会想捶我)完成课程任务,我还是准备写篇博客简单介绍一下课程项目。
    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);//实例化输入对象
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    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("请输入正确的指令");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    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";//数据库密码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    构造器,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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

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

    getQuestionsArray

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

    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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    User子类

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

    Primary

    //符号集
    private static final String[] symbols = {"+", "-", "*", "/"};
    
    • 1
    • 2
    @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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    JuniorHigh

    //符号集
    private static final String[] symbols = {"+", "-", "*", "/"};
    
    • 1
    • 2
    @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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    High

    //符号集
    private static final String[] symbols = {"+", "-", "*", "/"};
    private static final String[] highSymbols = {"sin", "cos", "tan"};
    
    • 1
    • 2
    • 3
    @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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    数据库

    user用户信息

    create table user
    (
        account  varchar(12) not null
            primary key,
        password varchar(15) null,
        identity varchar(5)  null
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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)
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    完整程序代码

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

    更多

    欢迎来访我的个人网站

  • 相关阅读:
    为大家推荐四款特别实用的Windows软件,下了就舍不得卸载
    CSS详解
    大专三年总结2019-2022
    凉鞋的 Unity 笔记 203. 变量的常用类型
    02 一些Keil ARM汇编伪指令
    GoLong的学习之路(十)语法之函数
    SpringBoot之文件上传(单文件与多文件上传的使用)
    Docker 容器常见故障排查及处理
    【艾特淘】手淘搜索新时代来了,开启搜索短视频时代,都是免费流量
    【下载器】NDM和IDM介绍(含安装包和教程)
  • 原文地址:https://blog.csdn.net/CatCatchCap/article/details/126831475