软件工程概论作业三
设计思想:
构造如下几个类:
试题类(class ShiTi):
属性:运算式、运算式结果、运算式逻辑运算顺序和运算数个数
公共方法:各个属性的set/get方法,
setTiMu方法中在设置了题目属性后立即设置运算数的个数并执行计算运算式结果的函数
在计算运算式结果的函数中,利用堆栈将运算式转换为后缀表达式进行计算,
每计算一个子表达式就将该表达式的运算数和运算符用逗号分隔添加到运算顺序字符串变量后
试题操作类(class ShiTiOperator):
公共方法:随机生成一个试题、判断两个试题是否逻辑相同
判断两个试题是否相同设计思想:
先比较两个运算式的运算数的个数,如果运算数个数不相同则运算式肯定不同,否则继续判断
比较两个运算式的答案是否相同,不同则返回FALSE,否则继续比较
获取两个试题的逻辑运算顺序字符串,并用‘,’对该字符串分割为一个字符串数组a
获取每一个子表达式,字符串数组a中没3个元素为一组子表达式,判断两运算式的该子表达式是否相同,
如果子表达式的运算符是加或乘则运算数可以翻转进行判断
分数类(class FenShu):
属性:分子、分母、该分数是否成立(如果分母是0就不成立)
公共方法:参数为分子分母的构造函数,参数为字符串的构造函数,分数乘除加减的函数
数据库单元类(class DBUtil):
公共方法:获取数据库的连接、释放资源
获取数据库连接的方法是将注册数据库驱动,获得一个数据库的连接进行封装,返回一个数据库连接对象
释放资源的方法是将释放查询数据库结果集对象、数据库操作对象和数据库连接对象进行封装
试题数据库操作类(ShiTiDAO):
公共方法:将一个试题对象集合插入到数据库
将一个试题对象集合插入到数据库的详细过程为:调用数据库单元类的获取数据库连接方法获取一个数据库连接对象,
构建一个sql语句为插入的数据库操作对象,
循环执行----(从试题对象中获取题目和答案等信息对参数进行设置,数据库操作对象执行)即可将多条试题信息插入到数据库
调用数据库单元类的释放资源方法释放资源
自定义异常类(class MyException):
用于对函数参数检测抛出异常,给出错误信息。
四则运算主类(class SiZe3):
公共方法:菜单、生成试题、验证统计文件中的试题
菜单方法:提供两种选项可以生成试题,可以对验证统计文件中的试题的正确性
生成试题方法:
调用试题操作类的生成试题的方法生成一个试题,并调用试题类的验证试题是否重复的方法进行重复校验,不重复则将该题目对象添加到题目list中
调用试题数据库操作类的插入数据的方法将生成的试题插入到数据库
验证统计文件中的试题方法:
从试题文件中读取试题题目,构建成试题对象,从试题对象中获取正确答案与答案文件中读取的答案进行比较,如果相同则该题目答案正确,将题号记录到一个整型的list中,否则该题目答案错误,将答案记录到另一个整型的list中。
最后将两个记录正确题号和记录错误题号的list信息写入到grade.txt文件中
代码:
试题类(class ShiTi):
package siZe3; import java.util.Stack; public class ShiTi { private int id; private String tiMu;// 题目 private String answer;// 答案 private String calculateOrder;// 运算式计算顺序,(后缀表达式) private int countNumber;// 运算符个数 public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTiMu() { return tiMu; } public void setTiMu(String tiMu) { this.tiMu = tiMu; try { expressCalculate(); } catch (MyException e) { // TODO Auto-generated catch block e.printStackTrace(); }// 计算答案 this.countNumber = (tiMu.split(" ").length + 1) / 2; } public String getAnswer() { return answer; } public void setAnswer(String answer) { this.answer = answer; } public String getCalculateOrder() { return calculateOrder; } public void setCalculateOrder(String calculateOrder) { this.calculateOrder = calculateOrder; } public int getCountNumber() { return countNumber; } public void setCountNumber(int countNumber) { this.countNumber = countNumber; } public ShiTi() { } // 表达式计算,参数为字符串类型的运算式 private void expressCalculate() throws MyException { if(this.tiMu == null) { throw new MyException("试题无效"); } String express = this.tiMu; Stack<String> num = new Stack<String>(); Stack<String> symbolS = new Stack<String>(); symbolS.push("#"); express += "#"; String order = ""; char ch; int i = 0; ch = express.charAt(i); while ((!symbolS.peek().equals("#")) || (ch != '#')) {// while循环开始 if (isNumber(ch)) {// 读到的不是空格,说明开始读运算数 String readNumStr = ""; while (true) { readNumStr += ch; ch = express.charAt(++i); if (ch == ' ' || ch == '#' || ch == ')') {// 读到的是空格,或,说明运算数结束 break; } } num.push(readNumStr); } else if (ch == ' ') { if ((i + 1) < express.length()) {// 未到字符串末尾 ch = express.charAt(++i); } }else {// 读到的是运算符 char compare = priorityCompare(symbolS.peek(), ch + ""); if (compare == '=') {// 若优先级相等,则说明ch是右括号,栈顶为左括号,此时将栈顶弹出,读取下一个字符 symbolS.pop(); ch = express.charAt(++i); } else if (compare == '>') {// ch的优先级小于栈顶的优先级,要说明栈顶的运算符应该先计算,所以应弹栈运算 // 弹出两个运算数,弹出一个运算符 String bStr = num.pop(); String aStr = num.pop(); String symbolT = symbolS.pop(); // 计算该字表达式 String c = yunSuan(aStr, bStr, symbolT); if (c.equals("ERROR")) {// 如果计算函数返回error则说明计算过程出现了负数,说明该运算式不符合要求,停止计算,计算结果为error,返回; this.answer = "ERROR"; return; } else {// 计算过程正常,则将计算结果压栈 order += aStr + "," + symbolT + "," + bStr + ",";// 将运算的子表达式加进运算顺序字符串中,操作数和操作符用逗号隔开 num.push(c); } } else if(compare == 'E') { this.answer = "ERROR"; return; } else {// 说明ch优先级大于栈顶元素的优先级,则应将ch压栈,读取下一个运算符 symbolS.push(ch + ""); if ((i + 1) < express.length()) { ch = express.charAt(++i); } } } } this.answer = num.pop(); this.calculateOrder = order; } // 判断ch是否为数字 private boolean isNumber(char ch) { if (ch >= '0' && ch <= '9') { return true; } return false; } /* * 子表达式计算,参数为两个运算数的字符串形式,和一个运算符,也为字符串类型 返回计算结果的字符串形式 * 如果减法运算出现负数,或除数为0,或分数的分母为0则返回ERROR * */ private String yunSuan(String aStr, String bStr, String symbol) throws MyException { if(aStr == null || bStr == null || symbol == null) { throw new MyException("子表达式出现错误!"); } int adivIndex = aStr.indexOf("/"); int bdivIndex = bStr.indexOf("/"); if ((adivIndex == -1) && (bdivIndex == -1)) {// a.b都是整数 int a = Integer.parseInt(aStr); int b = Integer.parseInt(bStr); switch (symbol.charAt(0)) { case '+': return a + b + ""; case '-': { if (a < b) { return "ERROR"; } return a - b + ""; } case '*': { return a * b + ""; } case '/': { if (b == 0) { return "ERROR"; } else if (a % b == 0) { return a / b + ""; } return new FenShu(a, b).toString(); } default: return "ERROR"; } } else {// a,b中存在分数,则将a,b都当做分数进行运算 FenShu a = new FenShu(aStr); FenShu b = new FenShu(bStr); switch (symbol.charAt(0)) { case '+': return a.add(b).toString(); case '-': { FenShu c = a.subtract(b); if(c.getNumerator() < 0) { return "ERROR"; } return c.toString(); } case '*': return a.multiply(b).toString(); case '/': return a.divide(b).toString(); default: return "ERROR"; } } } // 判断运算符优先级 private char priorityCompare(String a, String b) { char[][] priority = { { '>', '>', '<', '<', '<', '>', '>' }, { '>', '>', '<', '<', '<', '>', '>' }, { '>', '>', '>', '>', '<', '>', '>' }, { '>', '>', '>', '>', '<', '>', '>' }, { '<', '<', '<', '<', '<', '=', '>' }, { '>', '>', '>', '>', ' ', '>', '>' }, { '<', '<', '<', '<', '<', ' ', '=' } }; int a_index = index_symbol(a); int b_index = index_symbol(b); if(a_index == -1 || b_index == -1) { return 'E'; } return priority[a_index][b_index]; } // 获取运算符对应的下标 private int index_symbol(String a) { String p = "+-*/()#"; // System.out.println("判断运算符对应的下标:" + a); return p.indexOf(a); } }
试题操作类(class ShiTiOperator):
package siZe3; import java.util.Random; //试题类, /* * 方法有:生成一个试题,计算试题答案, * * */ public class ShiTiOperator { // 获取一个运算式 public static void main(String[] args) { ShiTi a = new ShiTi(); a.setTiMu("4 + 2 / 3 - 1");//设置题目a System.out.println("答案为:" + a.getAnswer()); // ShiTi b = new ShiTi(); // b.setTiMu("2 + 1 + 4 + 3");//设置题目b // System.out.println("是否重复判断结果为:" + calculateOrderSame(a, b)); } public static ShiTi getExpress(int maxNum, int hasKuoHao, int type) throws MyException { if(maxNum <= 0) { throw new MyException("最大数值应为正数"); } ShiTi stb = new ShiTi(); Random rd = new Random(); char[] fuHao = { '+', '-', '*', '/' }; while (true) { int[] bracket = null;// 存储括号位置 int expressLength = rd.nextInt(3) + 2;// 随机生成一个2~4之间的整数作为该运算式的运算数的个数 stb.setCountNumber(expressLength); String[] number = new String[expressLength];// 存储运算数的数组 String[] symbol = new String[expressLength - 1];// 存储运算符的数组 String express = ""; number[0] = getOperatorNumber(type, maxNum); for (int i = 0; i < expressLength - 1; i++) { symbol[i] = fuHao[rd.nextInt(4)] + "";// 生成运算符 number[i + 1] = getOperatorNumber(type, maxNum); } if (hasKuoHao == 1) { // 需要加括号 bracket = randomAddBracket(expressLength); } // 构建表达式 for (int i = 0; i < expressLength; i++) { // 添加左括号 if (hasKuoHao == 1) { for (int j = 0; j < bracket[i]; j++) { express += "("; } } express += number[i];// 加上运算数 // 添加右括号 if (hasKuoHao == 1) { for (int j = 0; j > bracket[i]; j--) { express += ")"; } } if (i != expressLength - 1) { express += " " + symbol[i] + " ";// 加运算符,并在两侧加空格来与运算数分隔 } } stb.setTiMu(express); if (!(stb.getAnswer().equals("ERROR"))) { // System.out.println("生成的运算式为:" + express + "=" + result[0]); return stb; } } } // 随机生成括号,参数为运算式的运算数的个数 private static int[] randomAddBracket(int length) throws MyException { if(length <= 1) { throw new MyException("运算式长度不能小于2"); } int[] brackets = new int[length]; for (int i = 0; i < brackets.length; i++) brackets[i] = 0; Random rd = new Random(); for (int i = 2; i < length; i++) {// 添加的括号长度(括号包围的运算数的个数) for (int j = 0; j < length - i + 1; j++) { int t = rd.nextInt(2);// 随机生成0或1,0代表不加括号,1代表加括号 if (t == 1) { if (brackets[j] >= 0 && brackets[j + i - 1] <= 0) {// 要加的括号的第一个运算数周围没有右括号,且 // 最后一个运算数周围没有左括号 int counteract1 = 0,counteract2 = 0,counteract3 = 0; for (int k = j; k < j + i; k++) {// 将要加的括号之间的所有运算数对应的brackets相加, // 如果和为0说明这个括号之间的括号是匹配的,不会出现括号交叉现象 counteract1 += brackets[k]; } for (int k = 0; k < j - 1; k++) {// 将要加的括号之前的所有运算数对应的brackets相加, // 如果和为0说明这个括号之间的括号是匹配的,不会出现括号交叉现象 counteract2 += brackets[k]; } for (int k = j + i; k < length; k++) {// 将要加的括号之后的所有运算数对应的brackets相加, // 如果和为0说明这个括号之间的括号是匹配的,不会出现括号交叉现象 counteract3 += brackets[k]; } if (counteract1 == 0 && counteract2 == 0 && counteract3 == 0) { brackets[j]++; brackets[j + i - 1]--; j += i; } } } } } return brackets; } // 随机生成一个运算数( type==0代表生成整数,type==1代表生成真分数,maxNum代表数值范围 0~(maxNum-1) ) private static String getOperatorNumber(int type, int maxNum) throws MyException { if(maxNum <= 0) { throw new MyException("最大数值应为正数"); } Random rd = new Random(); int a; while (true) { a = rd.nextInt(maxNum); if (type == 0) {// 随机生成一个整数 return "" + a; } else {// 随机生成一个真分数 if (a == 0) { continue; } int b = rd.nextInt(a); FenShu c = new FenShu(b, a); return c.toString(); } } } //-------------------------------------------------- public static boolean calculateOrderSame(ShiTi a, ShiTi b) throws MyException { if(a == null || b == null) { throw new MyException("试题无效!"); } //比较两个运算式的运算数个数 if(a.getCountNumber() != b.getCountNumber()) { return false; } //比较两运算式的答案是否相同 if(!a.getAnswer().equals(b.getAnswer())) { return false; } // 取出运算式的运算顺序字符串, String aorder = a.getCalculateOrder(); String border = b.getCalculateOrder(); // 将a,b运算式的运算顺序字符串进行分割,按序取出每一个运算数和运算符 String[] asplit = aorder.split(","); String[] bsplit = border.split(","); int n = a.getCountNumber() - 1;//共有n组子表达式 for(int i = 0;i < n;i++) { //取a运算式该子表达式的两个运算数a1,a2,运算符af,运算结果ar String a1 = asplit[0 + i * 3]; String af = asplit[1 + i * 3]; String a2 = asplit[2 + i * 3]; //取b运算式该子表达式的两个运算数b1,b2,运算符bf,运算结果br String b1 = bsplit[0 + i * 3]; String bf = bsplit[1 + i * 3]; String b2 = bsplit[2 + i * 3]; if(af.equals(bf)) { //两子表达式符号相同 if(a1.equals(b1) && a2.equals(b2)) { continue;//该子表达式相同,继续判断下一个子表达式 } else if( (af.equals("+") || af.equals("*")) && a1.equals(b2) && a2.equals(b1) ) { continue;//该子表达式相同,继续判断下一个子表达式 } else { return false; } } else { return false; } } return true; } //-------------------------------------------------- }
分数类(class FenShu):
package siZe3; public class FenShu { private int denominator, numerator; private boolean chengLi; public int getDenominator() { return denominator; } public void setDenominator(int denominator) { this.denominator = denominator; } public int getNumerator() { return numerator; } public void setNumerator(int numerator) { this.numerator = numerator; } public boolean isChengLi() { return chengLi; } public void setChengLi(boolean chengLi) { this.chengLi = chengLi; } public FenShu(int numerator, int denominator) { this.numerator = numerator; this.denominator = denominator; if (denominator == 0) { this.chengLi = false; } else { this.chengLi = true; yueJian(); } } // 根据字符串构造分数 public FenShu(String str) { if(str == null) { this.chengLi = false; } int index = str.indexOf("/"); if (index == -1) { this.numerator = Integer.parseInt(str); this.denominator = 1; this.chengLi = true; } else { this.denominator = Integer.parseInt(str.substring(index + 1)); if (this.denominator == 0) { chengLi = false; } else { chengLi = true; int zhengShu = str.indexOf("'"); if (zhengShu == -1) { // 没有整数部分 this.numerator = Integer.parseInt(str.substring(0, index)); } else { // 有整数部分 this.numerator = Integer.parseInt(str.substring(0, zhengShu)) * this.denominator + Integer.parseInt(str.substring(zhengShu + 1, index)); } yueJian(); } } } public FenShu() { } // 约简 private void yueJian() { int y = 1; for (int i = numerator; i > 1; i--) { if (numerator % i == 0 && denominator % i == 0) { y = i; break; } } // int nc = numerator,dc = denominator; // if(nc != 0){ // while(nc != dc - nc){ // y = dc - nc; // if(nc > y){ // dc = nc; // nc = y; // }else{ // dc = y; // } // } // y = nc; // numerator /= y; denominator /= y; } // 加 public FenShu add(FenShu b) { FenShu c = null; if (this.chengLi && b.isChengLi()) { int nNumerator = this.numerator * b.getDenominator() + this.denominator * b.getNumerator(); int nDenominator = this.denominator * b.getDenominator(); c = new FenShu(nNumerator, nDenominator); } else { c = new FenShu(); c.setChengLi(false); } return c; } // 减 public FenShu subtract(FenShu b) { FenShu c = null; if (this.chengLi && b.isChengLi()) { int nNumerator = this.numerator * b.getDenominator() - this.denominator * b.getNumerator(); int nDenominator = this.denominator * b.getDenominator(); c = new FenShu(nNumerator, nDenominator); } else { c = new FenShu(); c.setChengLi(false); } return c; } // 乘 public FenShu multiply(FenShu b) { FenShu c = null; if (this.chengLi && b.isChengLi()) { int nNumerator = this.numerator * b.getNumerator(); int nDenominator = this.denominator * b.getDenominator(); c = new FenShu(nNumerator, nDenominator); } else { c = new FenShu(); c.setChengLi(false); } return c; } // 除 public FenShu divide(FenShu b) { FenShu c = null; if (this.chengLi && b.isChengLi() && (b.getNumerator() != 0)) { int nNumerator = this.numerator * b.getDenominator(); int nDenominator = this.denominator * b.getNumerator(); c = new FenShu(nNumerator, nDenominator); } else { c = new FenShu(); c.setChengLi(false); } return c; } // 输出分数形式 public String toString() { if (this.chengLi) { if (numerator != 0) { if (numerator % denominator == 0) return "" + numerator / denominator; else if (numerator > denominator) { return (numerator / denominator) + "'" + (numerator % denominator) + "/" + denominator; } return numerator + "/" + denominator; } return "0"; } return "ERROR"; } }
数据库单元类(class DBUtil):
package siZe3; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; //连接数据库封装类, /* * 静态方法有获得数据库的连接、关闭数据库 */ public class DBUtil { private static final String driverName = "com.mysql.jdbc.Driver";// 连接MySQL数据库的驱动信息 private static final String userName = "root";// 连接MySQL数据库的用户名 private static final String userPwd = "490272";// 连接数据库的用户名的密码 private static final String dbName = "shiTiKu";// 数据库名 public static void main(String[] args) { Connection conn = getDBConnection(); PreparedStatement pstmt = null; ResultSet rs = null; closeRS(conn, pstmt, rs); } public static Connection getDBConnection() { // 定义连接URL String url1 = "jdbc:mysql://localhost:3306/" + dbName; String url2 = "?user=" + userName + "&password=" + userPwd; String url3 = "&useUnicode=true&characterEncoding=UTF-8"; String url = url1 + url2 + url3; Connection conn = null; try { Class.forName(driverName);// 注册驱动 conn = DriverManager.getConnection(url);// 获得连接 } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return conn; } // 释放资源 public static void closeRS(Connection conn, PreparedStatement pstmt, ResultSet rs) { try { if (rs != null) { rs.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { if (pstmt != null) { pstmt.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { if (conn != null) { conn.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
试题数据库操作类(class ShiTiDAO):
package siZe3; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; //试题数据库操作类,即对数据库进行增删改查的操作的类 public class ShiTiDAO { private String INSERT_SQL = "insert into shiTiKu01(tiMu,answer,calculateOrder,numberCount) values(?,?,?,?)"; private String SELECTBYNUMBERCOUNT_SQL = "select * from shiTiKu01 where numberCount=?"; private String SELECTALL = "select * from shiTiKu01"; // 向数据库中插入一条记录 public void insert(ShiTi stb) throws SQLException, MyException { if(stb == null) { throw new MyException("试题无效!"); } Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; conn = DBUtil.getDBConnection();// 获取连接 pstmt = conn.prepareStatement(INSERT_SQL);// 获取操作对象 // 设置字段值 pstmt.setString(1, stb.getTiMu()); pstmt.setString(2, stb.getAnswer()); pstmt.setString(3, stb.getCalculateOrder()); pstmt.setInt(4, stb.getCountNumber()); // 执行 pstmt.executeUpdate(); // 释放资源 DBUtil.closeRS(conn, pstmt, rs); } // 向数据库中插入多条记录,参数为list public void insert(List<ShiTi> list) throws SQLException, MyException { if(list == null) { throw new MyException("试题无效!"); } Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; conn = DBUtil.getDBConnection();// 获取连接 pstmt = conn.prepareStatement(INSERT_SQL);// 获取操作对象 // 设置字段值 for (ShiTi stb : list) { pstmt.setString(1, stb.getTiMu()); pstmt.setString(2, stb.getAnswer()); pstmt.setString(3, stb.getCalculateOrder()); pstmt.setInt(4, stb.getCountNumber()); pstmt.executeUpdate(); } // 执行 // 释放资源 DBUtil.closeRS(conn, pstmt, rs); } // 根据运算数个数查询,返回结果为list public List<ShiTi> selectByNumbercount(int count) throws SQLException, MyException { if(count < 2) { throw new MyException("运算数个数应大于等于2"); } Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; List<ShiTi> list = new ArrayList<ShiTi>(); conn = DBUtil.getDBConnection();// 获取连接 pstmt = conn.prepareStatement(SELECTBYNUMBERCOUNT_SQL);// 获取操作对象 // 设置字段值 pstmt.setInt(1, count); // 执行 rs = pstmt.executeQuery(); // 结果处理 ShiTi stb = null; while (rs.next()) { stb = new ShiTi(); stb.setTiMu(rs.getString("tiMu")); stb.setAnswer(rs.getString("answer")); stb.setCalculateOrder(rs.getString("calculateOrder")); stb.setCountNumber(rs.getInt("numberCount")); list.add(stb); } // 释放资源 DBUtil.closeRS(conn, pstmt, rs); return list; } //查询数据库中的全部记录 public List<ShiTi> selectAll() throws SQLException { Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; List<ShiTi> list = new ArrayList<ShiTi>(); conn = DBUtil.getDBConnection();// 获取连接 pstmt = conn.prepareStatement(SELECTALL);// 获取操作对象 // 执行 rs = pstmt.executeQuery(); // 结果处理 ShiTi stb = null; while (rs.next()) { stb = new ShiTi(); stb.setTiMu(rs.getString("tiMu")); stb.setAnswer(rs.getString("answer")); stb.setCalculateOrder(rs.getString("calculateOrder")); stb.setCountNumber(rs.getInt("numberCount")); list.add(stb); } // 释放资源 DBUtil.closeRS(conn, pstmt, rs); return list; } }
自定义异常类(class MyException):
package siZe3; public class MyException extends Exception { public MyException(String msg) { // TODO Auto-generated constructor stub super(msg); } }
主类(class SiZe3):
package siZe3; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.sql.SQLException; import java.util.ArrayList; import java.util.Date; import java.util.List; /* * 随机生成四则运算式3,2017年03月13日20:20:18 * * */ import java.util.Scanner; public class SiZe3 { static Scanner scan = new Scanner(System.in); public static void main(String[] args) throws FileNotFoundException { // TODO Auto-generated method stub menu(); scan.close(); } public static void menu() { System.out.println("1、判断文件中的题目及统计;2、随机生成题目"); int p = scan.nextInt(); if (p == 1) { System.out.println("请输入题目文件路径:"); String tiMuFileName = scan.next(); System.out.println("请输入答案文件路径:"); String answerFileName = scan.next(); try { panDuanTiMuFromFile(new File(tiMuFileName), new File(answerFileName)); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { System.out.println("0、整数式 1、分数式"); int type = scan.nextInt(); System.out.println("生成的运算式个数:"); int n = scan.nextInt(); System.out.println("是否有括号(1有,0没有)"); int hasKuoHao = scan.nextInt(); System.out.println("数值范围(最大数)"); int maxNum = scan.nextInt(); Date beginDate = new Date(); Date endDate = null; List<ShiTi> list = null; try { list = createYunSuanShi(hasKuoHao, maxNum, n, type); } catch (MyException e1) { // TODO Auto-generated catch block e1.printStackTrace(); System.out.println(e1); } // List<ShiTi> list2 = null; //List<ShiTiBean> list = createYunSuanShi(1, 20, 1000, 0); //将该list保存到数据库 ShiTiDAO st = new ShiTiDAO(); try { st.insert(list); endDate = new Date(); // list2 = st.selectAll(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (MyException e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println(e); } //比较从数据库读出来的数据与原数据是否相同 // boolean same = true; // if(list.size() == list2.size()) // { // for(int i = 0;i < list.size();i++) // { // ShiTi a = list.get(i); // ShiTi b = list.get(i); // if( !( (a.getTiMu().equals(b.getTiMu())) && (a.getAnswer().equals(b.getAnswer())) && (a.getCalculateOrder().equals(b.getCalculateOrder())) && (a.getCountNumber() == b.getCountNumber()))) // { // same = false; // break; // } // } // } // else // { // same = false; // } //显示试题信息 for (int i = 0; i < list.size(); i++) { ShiTi s = list.get(i); System.out.println((i + 1) + " : " + s.getTiMu() + " = " + s.getAnswer());// + " 运算顺序:" //+ s.getCalculateOrder() + " 运算数个数:" + s.getCountNumber()); } System.out.println("生成" + n + "个运算式并将数据插入到数据库共耗时:" + (endDate.getTime() - beginDate.getTime()) + "ms"); // System.out.println("从数据库读取出的数据与原数据是否相同:" + same); // System.out.println(""); //读取数据库中的全部试题 // System.out.println("生成" + n + "个运算式共耗时:" + (endDate.getTime() - beginDate.getTime()) + "ms"); } } //从文件中读取的题目和答案,判断答案是否正确,并将统计结果输出到文件Grade.txt文件 public static void panDuanTiMuFromFile(File tiMu, File answer) throws IOException { BufferedReader tiMuBR = new BufferedReader(new FileReader(tiMu)); BufferedReader answerBR = new BufferedReader(new FileReader(answer)); List<Integer> rightIndexList = new ArrayList<Integer>(); List<Integer> wrongIndexList = new ArrayList<Integer>(); int num = Integer.parseInt(tiMuBR.readLine()); for (int i = 0; i < num; i++) { ShiTi stb = new ShiTi(); String tiMuR = tiMuBR.readLine(); String[] tiMuRsz = tiMuR.split(":"); int index = Integer.parseInt(tiMuRsz[0]); stb.setTiMu(tiMuRsz[1]); String answerR = answerBR.readLine(); if (stb.getAnswer().equals(answerR)) { // 文件中的答案正确 // 将该题号添加到rightIndexList中 rightIndexList.add(index); } else { // 答案不正确,将该题号添加到wrongIndexList中 wrongIndexList.add(index); } } // 构造输出流对象,将结果信息输出到文件Grade.txt PrintWriter pw = new PrintWriter(new File("Grade.txt")); pw.print("Correct:" + rightIndexList.size() + "("); for (int i = 0; i < rightIndexList.size(); i++) { pw.print(rightIndexList.get(i)); if (i != (rightIndexList.size() - 1)) { pw.print(","); } else { pw.println(")"); } } pw.print("Wrong:" + wrongIndexList.size() + "("); for (int i = 0; i < wrongIndexList.size(); i++) { pw.print(wrongIndexList.get(i)); if (i != (wrongIndexList.size() - 1)) { pw.print(","); } else { pw.println(")"); } } System.out.println("已将统计结果输出到文件Grade.txt"); // 释放资源 pw.close(); answerBR.close(); tiMuBR.close(); } // 生成整数计算式添加限制条件,type为运算式类型 0代表整数式,1代表真分数式 public static List<ShiTi> createYunSuanShi(int hasKuoHao, int maxNum, int n, int type) throws MyException { int i = 0; if(n <= 0) { throw new MyException("运算数个数设置错误,应为正数"); } List<ShiTi> list = new ArrayList<ShiTi>(); ShiTi stb = null; // ShiTiDAO std = new ShiTiDAO(); while (i < n) { stb = ShiTiOperator.getExpress(maxNum, hasKuoHao, type); // 检验重复 boolean chongFu = false; for (int j = 0; j < i; j++) { ShiTi t = list.get(j); if (ShiTiOperator.calculateOrderSame(stb, t)) { chongFu = true; System.out.println("出现重复:计算式一:" + t.getTiMu() + " = " + t.getAnswer() + " 运算顺序:" + t.getCalculateOrder() + " 运算数个数:" + t.getCountNumber()); System.out.println("出现重复:计算式二:" + stb.getTiMu() + " = " + stb.getAnswer() + " 运算顺序:" + stb.getCalculateOrder() + " 运算数个数:" + stb.getCountNumber()); System.out.println("\n\n"); break; } } if (chongFu == false) { list.add(stb); i++; } } return list; } }
代码GitHub地址:https://github.com/liuxining/randomArithmeticalExpression
实现程序之前估计各个模块开发所花费的时间表:
实现程序之后再各个模块开发上所耗费的时间表:
测试:
对判断两个试题是否重复的函数进行测试:
测试方法:自己写两个重复和不重复的试题,对函数输出结果进行验证
(1)测试两个重复的试题
(2)测试两个不重复的试题:
(3)测试两个重复的试题:
初步测试判断运算式逻辑相同的函数正确
对计算表达式的值的函数进行判断:
判断方法,手动设置一个题目,程序会计算出答案,然后通过输出答案来验证计算结果的正确性。
(1)设置题目为整数式1 + 2 + (4 + 3),输出结果为:
(2)设置题目为分数式1/2 + 2/3 + 3/4
(3)设置题目为整数式,但会出现不整除现象的运算式4 + 2 / 3 - 1
对产生随机运算式的函数进行测试:
测试方法:改变产生运算数的个数的数量级,查看生成的试题是否正确并通过程序运行耗费的时间来检测程序运行速度
(1)产生100个,运算数范围0~10:
(2)产生1000个运算数范围0~20:
(3)产生10000个,运算数范围0~100
通过测试可以看出虽然题目都能生成但从耗时统计结果可以看出随着题目数量成倍增长,耗时也成倍增长。
将试题数据插入到数据库测试:
测试方法:将记录插入到数据库后,在全部读取出来,判断是否与原数据相同,相同则说明插入没有出现错误。但是每次测试之前要清空数据表。
(1)生成100条试题并插入数据库
(2)生成1000条试题并插入数据库
(3)生成10000条数据并插入到数据库:
初步测试结果:可以看出从数据库取出的数据与原数据相同,说明已经将试题成功插入数据库。