HNU个人互评项目:中小学数学卷子自动生成程序

发布时间 2023-09-21 22:08:17作者: 软1-黄展

一、前言

  HNU个人项目互评:我与软1张益诚同学结对,均使用java语言来完成中小学数学卷子自动生成程序项目,现在我将对其完成的代码进行分析和功能测试,希望在互评中能够学习到新的编程思路,认识到自己的不足,以此来提升自己的思维。

二、项目要求

HNU个人项目:中小学数学卷子自动生成程序

用户:

  小学、初中和高中数学老师。

功能:

  1、命令行输入用户名和密码,两者之间用空格隔开(程序预设小学、初中和高中各三个账号,具体见附表),如果用户名和密码都正确,将根据账户类型显示“当前选择为XX出题”,XX为小学、初中和高中三个选项中的一个。否则提示“请输入正确的用户名、密码”,重新输入用户名、密码;

  2、登录后,系统提示“准备生成XX数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):”,XX为小学、初中和高中三个选项中的一个,用户输入所需出的卷子的题目数量,系统默认将根据账号类型进行出题。每道题目的操作数在1-5个之间,操作数取值范围为1-100;

  3、题目数量的有效输入范围是“10-30”(含10,30,或-1退出登录),程序根据输入的题目数量生成符合小学、初中和高中难度的题目的卷子(具体要求见附表)。同一个老师的卷子中的题目不能与以前的已生成的卷子中的题目重复(以指定文件夹下存在的文件为准,见5);

  4、在登录状态下,如果用户需要切换类型选项,命令行输入“切换为XX”,XX为小学、初中和高中三个选项中的一个,输入项不符合要求时,程序控制台提示“请输入小学、初中和高中三个选项中的一个”;输入正确后,显示“”系统提示“准备生成XX数学题目,请输入生成题目数量”,用户输入所需出的卷子的题目数量,系统新设置的类型进行出题;

  5、生成的题目将以“年-月-日-时-分-秒.txt”的形式保存,每个账号一个文件夹。每道题目有题号,每题之间空一行;

  6、个人项目9月17日晚上10点以前提交至创新课程管理系统。提交方式:工程文件打包,压缩包名为“几班+姓名.rar”。迟交2天及以内者扣分,每天扣20%。迟交2天及以上者0分。

附表-1:账户、密码

账户类型

账户

密码

备注

小学

张三1

123

 

张三2

123

 

张三3

123

 

初中

李四1

123

 

李四2

123

 

李四3

123

 

高中

王五1

123

 

王五2

123

 

王五3

123

 

附表-2:小学、初中、高中题目难度要求

 

小学

初中

高中

 

难度要求

+,-,*./

平方,开根号

sin,cos,tan

 

备注

只能有+,-,*./()

题目中至少有一个平方或开根号的运算符

题目中至少有一个sin,costan的运算符

 

 

三、文件结构

Generation文件下:

  1、src文件夹下的exam文件夹中:有三个类,Main类、Testpaper类、User类

  (1)Main类:主函数及其他方法

  (2)Testpaper类:试卷类

  (3)User类:用户类

  2、对应用户名文件夹:存放该用户生成的题目文件,以txt文件形式保存

四、功能测试

1、登录功能测试:

  提示“请输入用户名和密码(用户名和密码之间请用一个空格隔开)”,当输入错误的用户名或者密码,会提示“用户名和密码错误”,然后可再次输入;

  当输入正确的用户名和密码时,登录成功,显示:

    “登录成功

    当前选择为XX出题

    1.出卷

    2.修改难度

    -1.退出”

    登录功能正常;

 

2、退出当前用户测试:

  在已经选择了出卷之后,会弹出输入生成题目数量的提示,此时输入-1后,会显示:

    “当前选择为小学出题

    1.出卷

    2.修改难度

    -1.退出”

  再次输入-1后应该直接提示要么切换账号,要么退出程序:

    ①输入-1,成功退出程序;

    ②输入1,提示“请输入用户名和密码(用户名和密码之间请用一个空格隔开)”,能够再次登录新的账号;

但  是这里存在问题:在第一次输入-1的时候,就应该直接提示输入1切换账号,输入-1退出程序才对,所以这个部分需要修改,有待完善;

 

3、出题测试:

  当登录成功时,选择1,选择出卷,则提示该用户准备生成该用户对应的学生类型(小学、初中、高中)的题目,输入需要生成的题目数量,小于10或者大于30都会提示输入题目数量不在10-30范围内,当输入数量在范围内的时候,则会成功生成题目存储在txt文件当中,并提示:

    “创建试卷成功

    当前为选择为XX出题

    1.出卷

    2.修改难度

    -1.退出”

  如下分别为生成的小学、初中、高中的题目(txt文件),存在于以对应用户名为名字的文件夹中,保存名称形式为“年-----.txt”,如下:

    a、小学数学题目:

    可以看到加减乘除都包含着,也存在含有()的情况;

    但是存在问题:

      <1>()只有一对,可以是多对;还有就是()直接在式子的最左和最右,没有意义,如:3.(74÷77÷67-97)=

      <2>当只有两个操作数的时候,依旧有括号对存在的情况,如6.(80+98)=

    b、初中数学题目:

    可以看到加减乘除都包含着,也存在含有()的情况;

    但是存在问题:

      <1>()只有一对,可以是多对;还有就是()直接在式子的最左和最右,没有意义,如:4.(√52-80-24)=

      <2>存在没有出现平方或者开跟的数学式子,如:10.59+63=

    c、高中数学题目:

    可以看到加减乘除都包含着,也存在含有()的情况;

    但是存在问题:

      <1>()直接在式子的最左和最右,没有意义,如:5.(16÷cos33-94×(97×8))=

      <2>存在没有出现sincostan数学式子,如:1.98×6÷85=

  出题功能还有待完善;

 

4、切换出题难度测试:

 

  输入2选择修改难度后,输入切换为XX,即可切换到对应的阶段;

  若输入内若不是“切换为XX”则会显示“请输入小学、初中和高中三个选项中的一个”:

 

  此时只要根据提示输入XX即可切换难度;

  切换出题难度功能正常;

 

五、代码分析

src文件夹下的exam文件夹中:有三个类,Main类、Testpaper类、User类

  (1)Testpaper类:

    ①成员变量:

      a、num:私有整数变量,用于存储试卷题目的数量。

      b、Question:一个私有的ArrayList,用于存储试卷中的题目,每个题目都是一个StringBuffer对象。

      c、Level:一个私有的字符串变量,用于表示试卷的难度级别,不满足代码规范,应该是全部小写。

    ②构造方法:这个类没有明确的构造方法,所以默认的无参构造方法会被创建。

    ③getLevel 和 setLevel 方法:

      a、getLevel:用于获取试卷的难度级别。

      b、setLevel:用于设置试卷的难度级别。

    ④setQuestion 方法:

      setQuestion:用于将题目添加到试卷中。这个方法使用了一个while循环,确保试卷中不会包含重复的题目。如果试卷中已经包含了相同的题目(根据内容判断),则不会添加重复的题目。

    ⑤check 方法:

      check:用于检查试卷中是否包含相同的题目。如果试卷中包含了传入的题目(根据内容判断),则返回true,否则返回false。

    ⑥setNum 和 getNum 方法:

      a、setNum:用于设置试卷的题目数量。

      b、getNum:用于获取试卷的题目数量。

    ⑦getQuestion 方法:

      getQuestion:用于获取试卷中指定索引位置的题目。

package exam;

import java.util.ArrayList;

public class Testpaper {
    private int num;
    private ArrayList<StringBuffer> Question=new ArrayList<>();
    private String Level;
    public String getLevel() {
        return Level;
    }
    public void setLevel(String level) {
        this.Level = level;
    }
    public void setQuestion(StringBuffer str){
        while (!Question.contains(str)){
            Question.add(str);
        }

    }
    public boolean check(StringBuffer str){
        return Question.contains(str);
    }
    public void setNum(int num){
        this.num=num;
    }
    public int getNum(){
        return num;
    }
    public StringBuffer getQuestion(int index1){
        return Question.get(index1);
    }
}

 

  (2)User类:定义了三个属性:

    <1>name:用户名,String

    <2>password:密码,String

    <3>level:类型(小学、初中、高中),String

   方法:

    <1>获取用户名:String getName()

    <2>设置用户名:void setName(String name)

    <3>获取密码:String getPassword()

    <4>设置密码:void setPassword(String password)

    <5>获取类型:String getLevel()

    <6>设置类型:void setLevel(String level)

    <7>比对用户名和密码:boolean compare(User user)

package exam;

public class User {
    private String name;
    private String password;
    private String level;
    public String getName() {
        return name;
    }
    public void setName(String name) {

        this.name = name;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getLevel() {
        return level;
    }
    public void setLevel(String level) {
        this.level = level;
    }

    public boolean compare(User user){

        if (this.name.equals(user.getName()) && this.password.equals(user.getPassword())){
            return true;
        }
        else  return false;
    }
}

 

  (3)Main类:主函数及其他方法

package exam;

import java.io.File;
import java.io.IOException;
import java.io.BufferedWriter;
import java.io.FileWriter;

import java.text.SimpleDateFormat;

import java.util.Scanner;
import java.util.Random;
import java.util.Date;

public class Main {
    static Scanner   input = new Scanner(System.in);
    ...
}

    ①login 方法:

     login 方法用于用户登录。它会要求用户输入用户名和密码,然后检查输入的用户名和密码是否匹配已经存在的用户数组(user1、user2、user3)。如果匹配成功,用户将被登录,并且设置了用户的难度级别。如果匹配失败,用户将被提示重新输入。

public static void login(User tempUser, User[] user1, User[] user2, User[] user3){
        System.out.println("请输入用户名和密码(用户名和密码之间请用一个空格隔开)");
        int flag=0;
        while (true) {
            if (flag!=0){
                System.out.println("请重新输入用户名和密码(用户名和密码之间请用一个空格隔开)");
            }
            String name = input.next();   //输入的用户名
            tempUser.setName(name);
            String password = input.next(); //输入的密码
            tempUser.setPassword(password);
            boolean sign = false;
            for (int i = 0; i < 3; i++) {
                if (tempUser.compare(user1[i])) {
                    sign = true;
                    tempUser.setLevel(user1[i].getLevel());
                    break;
                } else if (tempUser.compare(user2[i])) {
                    sign = true;
                    tempUser.setLevel(user2[i].getLevel());
                    break;
                } else if (tempUser.compare(user3[i])) {
                    sign = true;
                    tempUser.setLevel(user3[i].getLevel());
                    break;
                }
            }
            if (sign){
                System.out.println("登录成功");
                break;
            }
            else {
                System.out.println("用户名或密码错误");
                flag++;
            }
        }
    }

    ②init 方法:

     init 方法用于初始化用户数组(user1、user2、user3)的内容。它创建了一些示例用户(张三1、张三2、张三3、李四1、李四2、李四3、王五1、王五2、王五3),并为每个用户设置了用户名、密码和难度级别。

public static void init(User[] user1, User[] user2, User[] user3) {
        for (int i = 0; i < 3; i++) {
            user1[i] = new User();
            user1[i].setName("张三" + (i + 1));
            user1[i].setPassword("123");
            user1[i].setLevel("小学");
            user2[i] = new User();
            user2[i].setName("李四" + (i + 1));
            user2[i].setPassword("123");
            user2[i].setLevel("初中");
            user3[i] = new User();
            user3[i].setName("王五" + (i + 1));
            user3[i].setPassword("123");
            user3[i].setLevel("高中");
        }
    }

    ③changeLevel 方法:

     changeLevel 方法用于更改用户的难度级别。用户可以输入新的难度级别,如果输入不是"小学"、"初中"或"高中"之一,程序会一直要求用户重新输入,直到输入一个有效的难度级别。如果输入是"切换为小学"、"切换为初中"或"切换为高中",则提取级别并设置用户的难度级别。

public static void changeLevel(User user){
        String level=input.next();
        String l1="切换为小学";
        String l2="切换为初中";
        String l3="切换为高中";
        if (!level.equals(l1)&&!level.equals(l2)&&!level.equals(l3)){
            while (true){
                System.out.println("请输入小学、初中和高中三个选项中的一个");
                level=input.next();
                if (!level.equals("小学")&&!level.equals("初中")&&!level.equals("高中")){
                    continue;
                }
                else {
                    user.setLevel(level);
                    break;
                }
            }
        }
        else{
            String newLevel=level.substring(3);
            System.out.println(newLevel);
            user.setLevel(newLevel);
        }
    }

    ④createTestPaper 方法:

     createTestPaper 方法用于生成数学试卷(Testpaper对象)。根据用户的难度级别,它生成特定级别的数学题目,并将这些题目添加到试卷中。

public static void createTestPaper(Testpaper testpaper, User user, int num) {
        if (user.getLevel().equals("小学")) {
            testpaper.setLevel(user.getLevel());
            testpaper.setNum(num);
            for (int i=0;i<num;i++){
                StringBuffer temp;
                temp= createPrimary(i+1);
                while(testpaper.check(temp)){
                    temp= createPrimary(i+1);
                }
                testpaper.setQuestion(temp);
            }
        }
        if (user.getLevel().equals("初中")) {
            testpaper.setLevel(user.getLevel());
            testpaper.setNum(num);
            for (int i=0;i<num;i++){
                StringBuffer temp;
                temp= createJunior(i+1);
                while(testpaper.check(temp)){
                    temp= createJunior(i+1);
                }
                testpaper.setQuestion(temp);
            }
        }
        if (user.getLevel().equals("高中")) {
            testpaper.setLevel(user.getLevel());
            testpaper.setNum(num);
            for (int i=0;i<num;i++){
                StringBuffer temp;
                temp= createHigh(i+1);
                while(testpaper.check(temp)){
                    temp= createHigh(i+1);
                }
                testpaper.setQuestion(temp);
            }
        }
    }

    ⑤writeTestPaper 方法:

     writeTestPaper 方法用于将生成的试卷写入文件。它首先创建一个文件夹,以用户的用户名为名称,然后创建一个以当前日期和时间命名的文本文件,将试卷的内容写入文件中。

public static void writeTestPaper(Testpaper testpaper, User user){
        String time =  new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date().getTime());
        File file=new File(user.getName());
        if (!file.exists()){
            file.mkdirs();
        }
        String fname=file+"/"+time+".txt";
        try(BufferedWriter writer = new BufferedWriter(new FileWriter(fname))){
            for (int i = 0; i <testpaper.getNum(); i++) {
                writer.write(testpaper.getQuestion(i).toString());
                writer.newLine();
                writer.newLine();
            }
            System.out.println("创建试卷成功");
        }catch(IOException e) {
            e.printStackTrace();
        }

    }

    ⑥createPrimary、createJunior 和 createHigh 方法:

     这些方法用于生成不同难度级别的数学题目。它们创建了包含数字和运算符的字符串,并按照一定的规则组合它们,以生成数学题目。

      <1>createPrimary:

public static StringBuffer createPrimary(int num){
        int flag1=0;
        int flag2=0;
        char[] Operator={'+','-','×','÷','(',')'};
        StringBuffer question=new StringBuffer();
        question.append(num+".");
        Random opnum=new Random();
        Random qnum=new Random();
        int t=opnum.nextInt(4)+2;
        int sign=t;
        while (t>0){
            int temp=(opnum.nextInt(6));
            if (temp==4&&t==sign){
                question.append(Operator[temp]);
                flag1++;
            }
            question.append(qnum.nextInt(100)+1);
            if (temp==4&&t!=sign&&t>2){
                question.append(Operator[opnum.nextInt(4)]);
                question.append(Operator[temp]);
                flag1++;
                t--;
                continue;
            }
            if (temp==5&&flag2<flag1){
                question.append(Operator[temp]);
                flag2++;
            }
            if (t==1){break;}
            question.append(Operator[opnum.nextInt(4)]);
            t--;
        }
            for (int i=flag1-flag2;i>0;i--){
                question.append(')');
            }
            question.append('=');

            return question;
    }

      <2>createjunior:

 public static StringBuffer createJunior(int num){
        int flag1=0;
        int flag2=0;
        char[] Operator={'+','-','×','÷','(',')'};
        String[] Op={"²","√","sin","cos","tan"};
        StringBuffer question=new StringBuffer();
        question.append(num+".");
        Random opnum=new Random();
        Random qnum=new Random();
        Random randd=new Random();
        int index=99;
        int t=opnum.nextInt(4)+2;
        int sign=t;
        while (t>0){
            int temp=(opnum.nextInt(6));
            if (temp==4&&t==sign){
                question.append(Operator[temp]);
                flag1++;
            }
            if (randd.nextInt(2)==1){
                index=randd.nextInt(2);
                if (index==0){
                    question.append((qnum.nextInt(100)+1)+Op[index]);
                }
                else question.append(Op[index]+(qnum.nextInt(100)+1));
            }
            else question.append(qnum.nextInt(100)+1);
            if (temp==4&&t!=sign&&t>2){
                question.append(Operator[opnum.nextInt(4)]);
                question.append(Operator[temp]);
                flag1++;
                t--;
                continue;
            }
            if (temp==5&&flag2<flag1){
                question.append(Operator[temp]);
                flag2++;
            }
            if (t==1){break;}
            question.append(Operator[opnum.nextInt(4)]);
            t--;
        }
        for (int i=flag1-flag2;i>0;i--){
            question.append(')');
        }
        question.append('=');
        return question;
    }

      <3>createHigh:

public static StringBuffer createHigh(int num){
        int flag1=0;
        int flag2=0;
        char[] Operator={'+','-','×','÷','(',')'};
        String[] Op={"²","√","sin","cos","tan"};
        StringBuffer question=new StringBuffer();
        question.append(num+".");
        Random opnum=new Random();
        Random qnum=new Random();
        Random randd=new Random();
        int index=99;
        int t=opnum.nextInt(4)+2;
        int sign=t;
        while (t>0){
            int temp=(opnum.nextInt(6));
            if (temp==4&&t==sign){
                question.append(Operator[temp]);
                flag1++;
            }
            if (randd.nextInt(2)==1){
                index=randd.nextInt(5);
                if (index==0){
                    question.append((qnum.nextInt(100)+1)+Op[index]);
                }
                else question.append(Op[index]+(qnum.nextInt(100)+1));
            }
            else question.append(qnum.nextInt(100)+1);
            if (temp==4&&t!=sign&&t>2){
                question.append(Operator[opnum.nextInt(4)]);
                question.append(Operator[temp]);
                flag1++;
                t--;
                continue;
            }
            if (temp==5&&flag2<flag1){
                question.append(Operator[temp]);
                flag2++;
            }
            if (t==1){break;}
            question.append(Operator[opnum.nextInt(4)]);
            t--;
        }
        for (int i=flag1-flag2;i>0;i--){
            question.append(')');
        }
        question.append('=');
        return question;
    }

    ⑦menu 方法:

     menu 方法显示用户的当前难度级别,并提供选项菜单,允许用户选择生成试卷或修改难度级别。

public static void menu(User tempUser){
        System.out.println("当前选择为"+tempUser.getLevel()+"出题");
        System.out.println("1.出卷");
        System.out.println("2.修改难度");
        System.out.println("-1.退出");
    }

    ⑧main 方法:

     main 方法是程序的入口点。它初始化用户数组,然后通过登录方法验证用户身份。之后,它进入一个无限循环,提供不同的选项供用户选择。用户可以选择生成试卷、修改难度级别或退出程序。

public static void main(String[] args) {
        User[] user1 = new User[3];
        User[] user2 = new User[3];
        User[] user3 = new User[3];
        init(user1,user2,user3);

        User tempUser = new User();
        login(tempUser,user1,user2,user3);

        while (true){
            menu(tempUser);
            int sign=input.nextInt();
            switch (sign){
                case 1:
                    Testpaper tes=new Testpaper();
                    while(true){
                    System.out.println("准备生成"+tempUser.getLevel()+"数学题目,请输入题目数量,有效输入范围是“10-30”(含10,30,或-1退出登录)");
                    int num=input.nextInt();
                        if (num==-1){
                            break;
                        }
                    if (num<10||num>30){
                        continue;
                    }

                    else {
                        createTestPaper(tes,tempUser,num);
                        writeTestPaper(tes,tempUser);
                        break;
                    }
                }
                break;
                case 2: changeLevel(tempUser); break;
                case -1:
                    if (sign==-1){
                        while (true){
                            System.out.println("若想切换账号,请输入1,退出程序输入-1");
                            int flag=input.nextInt();
                            if (flag==-1){
                                break;
                            }
                            else if (flag==1){
                                login(tempUser,user1,user2,user3);
                                sign=1;
                                break;
                            }
                        }
                    }
                    break;
                default:
                    System.out.println("请输入正确指令");
                     break;
            }
            if (sign==-1){
                System.out.println("已退出程序");
                break;
            }
        }
    }

 

六、总结

  总体而言,此项目完成了所有功能,并进行了一定的优化,但是有些细节有待优化。
  通过此项目,我们最重要的是要意识到规范编程的重要性。规范编程可以提高代码质量、增加可维护性和可读性、利于团队协作并且提高我们编程的效率。此次个人项目编程是我们规范编程的试手,希望在未来的编程中,我们能一直坚持规范编程。