为了使用简单数学表达式做验证码,就要计算生成的表达式的计算结果,就去看了下数学表达式计算的方法。本来只计算简单的整型加减法的,但是最后整理了下,能计算小数和乘除。
核心算法:
以整数计算为例
public Integer calculate(String[] expr) throws IllegalOperandException {
Stack numStk = new Stack(); //操作数栈
Stack opStk = new Stack(); //操作符栈
for (String s : expr) {
if (isOperand(s)) { //遇到数字,直接压栈
int num = Integer.parseInt(s);
numStk.push(num);
} else if (s.equals("(")) { //遇到左括号,直接压栈
opStk.push(s);
} else if (s.equals(")")) {
while (true) {
String op = opStk.pop();
if (op.equals("(")) //弹出左括号,结束
break;
else { //不是左括号,则为操作符,进行运算
int right = numStk.pop();
int left = numStk.pop();
int re = operate(left, right, op);
numStk.push(re);
}
}
} else { //+ - / *
int priority = getPriority(s);//当前操作符优先级
while (true) {
if (opStk.empty() || priority > getPriority(opStk.peek())) { //操作符栈为空 或 当前符号优先级比栈顶符号优先级高,直接入栈
opStk.push(s);
break;
} else {
int right = numStk.pop();
int left = numStk.pop();
String op = opStk.pop();
int re = operate(left, right, op);
numStk.push(re);
}
}
}
}
while (!opStk.empty()) { //对栈中剩余数进行运算
int right = numStk.pop();
int left = numStk.pop();
String op = opStk.pop();
int re = operate(left, right, op);
numStk.push(re);
}
return numStk.pop();
}
计算结果:
验证:
代码结构
CalculatorUtil:包含了工具方法
其他两种计算器的计算实现都是参照整数计算来改写的,把Integer换成了Double或BigDecimal而已
源文件们
Calculators.java
package my.Calculator;
public class Calculators {
public static SimpleIntegerCalculator newSimpleIntegerCalculator() {
return new SimpleIntegerCalculator();
}
public static DoubleCalculator newDoubleCalculator() {
return new DoubleCalculator();
}
public static PreciseCalculator newPreciseCalculator() {//默认保留6位小数,四舍五入
return new PreciseCalculator();
}
public static PreciseCalculator newPreciseCalculator(int scale) {//自己指定保留小数位数,四舍五入
return new PreciseCalculator(scale);
}
public static PreciseCalculator newPreciseCalculator(int scale, int roundMode) {//自己指定保留小数位数和舍入方式
return new PreciseCalculator(scale, roundMode);
}
}
CalculatorUtil.java
package my.Calculator;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import my.Calculator.Exceptions.IllegalExpressionException;
import my.Calculator.Exceptions.IllegalOperandException;
class CalculatorUtil {
//得到操作符优先级
static int getPriority(String op) { //获取操作符优先级
if (op.equals("+") || op.equals("-")) {
return 1;
} else if (op.equals("*") || op.equals("/")) {
return 2;
} else if (op.equals("(")) {
return 0;
}
return 0;
}
//判断是操作数
static boolean isOperand(String str) throws IllegalOperandException {
boolean isOperand = false;
if (str.charAt(0) == '-') { //处理负数
if (str.length() == 1) {
return false;
} else {
for (int i = 1; i < str.length(); i++) {
if (str.charAt(i) == '.') {
if (!isOperand) {
isOperand = true;
continue;
} else
throw new IllegalOperandException("\"" + str + "\"" + "is not a operand");
}
if (!Character.isDigit(str.charAt(i))) {
return false;
}
}
}
} else {
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == '.') {
if (!isOperand) {
isOperand = true;
continue;
} else
throw new IllegalOperandException("\"" + str + "\"" + "is not a operand");
}
if (!Character.isDigit(str.charAt(i))) {
return false;
}
}
}
return true;
}
//解析表达式为数组
static String[] parseExpression(String expression) throws IllegalExpressionException {
if (!validate(expression))
throw new IllegalExpressionException();
expression = expression.replace(" ", "");//去掉空格
List exp = new ArrayList();
int l = expression.length();//表达式长度
int i = 0;
while (true) {
if (i >= l)
break;
char c = expression.charAt(i);
if (Character.isDigit(c)) {//如果开头为数字,则找全数字部分
StringBuilder sb = new StringBuilder();
for (int j = i; j < l; j++) {
char cc = expression.charAt(j);
if (Character.isDigit(cc) || cc == '.') {
sb.append(cc);
i++;
} else {
break;
}
}
exp.add(sb.toString());
}
if (i >= l)
break;
c = expression.charAt(i);
if (c == '-') {
if (i > 0 &&//不是第一个才有可能是负号,所以i>0才可能是减号
(Character.isDigit(expression.charAt(i - 1)) ||
expression.charAt(i - 1) == ')')) {//前面一个字符为数字或右括号,那么就为操作符减号 如 8-1,(1+2)-1
exp.add(String.valueOf(c));
i++;
} else {//否则为负号
StringBuilder sb = new StringBuilder();
sb.append(c);//负号
i++;
for (int j = i; j < l; j++) {//找数字部分
char cc = expression.charAt(j);
if (Character.isDigit(cc) || cc == '.') {
sb.append(cc);
i++;
} else {
break;
}
}
exp.add(sb.toString());
}
} else {
//剩下的就是+ * / 了
exp.add(String.valueOf(expression.charAt(i)));
i++;
}
}
return exp.toArray(new String[exp.size()]);
}
//两个数运算
static Integer operateInteger(Integer a, Integer b, String op) {
Integer result = 0;
if (op.equals("+"))
result = a + b;
else if (op.equals("-"))
result = a - b;
else if (op.equals("*"))
result = a * b;
else if (op.equals("/"))
result = a / b;
return result;
}
static Double operateDouble(Double a, Double b, String op) {
Double result = 0.0;
if (op.equals("+"))
result = a + b;
else if (op.equals("-"))
result = a - b;
else if (op.equals("*"))
result = a * b;
else if (op.equals("/"))
result = a / b;
return result;
}
static BigDecimal operateBigDecimal(BigDecimal a, BigDecimal b, String op) {
BigDecimal result = null;
if (op.equals("+"))
result = a.add(b);
else if (op.equals("-"))
result = a.subtract(b);
else if (op.equals("*"))
result = a.multiply(b);
else if (op.equals("/"))
result = a.divide(b, 32, BigDecimal.ROUND_HALF_UP);
return result;
}
//计算整数表达式
static Integer calculateInteger(String[] expr) throws Exceptions.IllegalOperandException {
Stack numStk = new Stack(); //操作数栈
Stack opStk = new Stack(); //操作符栈
for (String s : expr) {
if (isOperand(s)) { //遇到数字,直接压栈
Integer num = Integer.valueOf(s);
numStk.push(num);
} else if (s.equals("(")) { //遇到左括号,直接压栈
opStk.push(s);
} else if (s.equals(")")) {
while (true) {
String op = opStk.pop();
if (op.equals("(")) //弹出左括号,结束
break;
else { //不是左括号,则为操作符,进行运算
Integer right = numStk.pop();
Integer left = numStk.pop();
Integer re = operateInteger(left, right, op);
numStk.push(re);
}
}
} else { //+ - / *
Integer priority = getPriority(s);//当前操作符优先级
while (true) {
if (opStk.empty() || priority > getPriority(opStk.peek())) { //操作符栈为空 或 当前符号优先级比栈顶符号优先级高,直接入栈
opStk.push(s);
break;
} else {
Integer right = numStk.pop();
Integer left = numStk.pop();
String op = opStk.pop();
Integer re = operateInteger(left, right, op);
numStk.push(re);
}
}
}
}
while (!opStk.empty()) { //对栈中剩余数进行运算
Integer right = numStk.pop();
Integer left = numStk.pop();
String op = opStk.pop();
Integer re = operateInteger(left, right, op);
numStk.push(re);
}
return numStk.pop();
}
//计算double表达式
static Double calculateDouble(String[] expr) throws IllegalOperandException {
Stack numStk = new Stack<>(); //操作数栈
Stack opStk = new Stack(); //操作符栈
for (String s : expr) {
if (isOperand(s)) { //遇到数字,直接压栈
double num = Double.parseDouble(s);
numStk.push(num);
} else if (s.equals("(")) { //遇到左括号,直接压栈
opStk.push(s);
} else if (s.equals(")")) {
while (true) {
String op = opStk.pop();
if (op.equals("(")) //弹出左括号,结束
break;
else { //不是左括号,则为操作符,进行运算
double right = numStk.pop();
double left = numStk.pop();
double re = operateDouble(left, right, op);
numStk.push(re);
}
}
} else { //+ - / *
int priority = getPriority(s);//当前操作符优先级
while (true) {
if (opStk.empty() || priority > getPriority(opStk.peek())) { //操作符栈为空 或 当前符号优先级比栈顶符号优先级高,直接入栈
opStk.push(s);
break;
} else {
double right = numStk.pop();
double left = numStk.pop();
String op = opStk.pop();
double re = operateDouble(left, right, op);
numStk.push(re);
}
}
}
}
while (!opStk.empty()) { //对栈中剩余数进行运算
double right = numStk.pop();
double left = numStk.pop();
String op = opStk.pop();
double re = operateDouble(left, right, op);
numStk.push(re);
}
return numStk.pop();
}
//判断为空或者空
static boolean validate(String... s) {
for (String s1 : s) {
if (s1 == null || s1 == "")
return false;
}
return true;
}
}
SimpleIntegerCalculator.java
package my.Calculator;
import my.Calculator.Exceptions.IllegalExpressionException;
import my.Calculator.Exceptions.IllegalOperandException;
import static my.Calculator.CalculatorUtil.*;
public class SimpleIntegerCalculator {
/**
* 计算无分割符的表达式,如:2*(-32+1),由于要解析表达式,所以性能更低
*
* @param expression
* @return
* @throws IllegalOperandException
* @throws IllegalExpressionException
*/
public Integer calculate(String expression) throws IllegalOperandException, IllegalExpressionException {
if (!validate(expression))
throw new IllegalExpressionException();
String[] expr = parseExpression(expression);
return calculateInteger(expr);
}
/**
* 计算有分隔符的表达式 如: 2;*;(;-32;+;1;)
*
* @param expression 表达式
* @param separator 分隔符
* @return
* @throws IllegalOperandException
* @throws IllegalExpressionException
*/
public Integer calculate(String expression, String separator) throws IllegalOperandException, IllegalExpressionException {
return calculate(expression, separator, true);
}
/**
* 计算有分隔符的表达式
*
* @param expression 表达式
* @param separator 每个操作符,数字间的分隔符,不能以空格作为分隔符
* @param removeSpace 是否去掉空格。如果不能确定表达式中是否有空格,则要指定 removeSpace 为true;如果表达式规则,则指定为false,这样会带来性能上的略微提升
* @return
* @throws IllegalOperandException
* @throws IllegalExpressionException
*/
public Integer calculate(String expression, String separator, boolean removeSpace) throws IllegalOperandException, IllegalExpressionException {
if (!validate(expression))
throw new IllegalExpressionException();
if (removeSpace)
expression = expression.replace(" ", "");
String[] expr = expression.split(separator);
return calculateInteger(expr);
}
}
DoubleCalculator.java
package my.Calculator;
import my.Calculator.Exceptions.IllegalExpressionException;
import my.Calculator.Exceptions.IllegalOperandException;
import static my.Calculator.CalculatorUtil.*;
public class DoubleCalculator {
/**
* 计算有分隔符的表达式 如: 2;*;(;-32;+;1;)
*
* @param expression 表达式
* @param separator 分隔符
* @return
* @throws IllegalOperandException
* @throws IllegalExpressionException
*/
public Double calculate(String expression, String separator) throws IllegalOperandException, IllegalExpressionException {
return calculate(expression, separator, true);
}
/**
* 计算有分隔符的表达式
*
* @param expression 表达式
* @param separator 每个操作符,数字间的分隔符,不能以空格作为分隔符
* @param removeSpace 是否去掉空格。如果不能确定表达式中是否有空格,则要指定 removeSpace 为true;如果表达式规则,则指定为false,这样会带来性能上的略微提升
* @return
* @throws IllegalOperandException
* @throws IllegalExpressionException
*/
public Double calculate(String expression, String separator, boolean removeSpace) throws IllegalOperandException, IllegalExpressionException {
if (!validate(expression))
throw new IllegalExpressionException();
if (removeSpace)
expression = expression.replace(" ", "");
String[] expr = expression.split(separator);
return calculateDouble(expr);
}
/**
* 计算无分割符的表达式,如:2*(-32+1),由于要解析表达式,所以性能更低
*
* @param expression
* @return
* @throws IllegalOperandException
* @throws IllegalExpressionException
*/
public Double calculate(String expression) throws IllegalOperandException, IllegalExpressionException {
if (!validate(expression))
throw new IllegalExpressionException();
String[] expr = parseExpression(expression);
return calculateDouble(expr);
}
}
PreciseCalculator.java
package my.Calculator;
import java.math.BigDecimal;
import java.util.Stack;
import my.Calculator.Exceptions.IllegalExpressionException;
import my.Calculator.Exceptions.IllegalOperandException;
import static my.Calculator.CalculatorUtil.*;
public class PreciseCalculator {
public final static int HALF_UP = BigDecimal.ROUND_HALF_UP; //四舍五入
public final static int HALF_DOWN = BigDecimal.ROUND_HALF_DOWN;//五舍六入
public final static int HALF_EVEN = BigDecimal.ROUND_HALF_EVEN;//银行家舍入方式,相比四舍五入,舍入概率更一致
public final static int DOWN = BigDecimal.ROUND_DOWN;//直接舍去后面的数
public final static int UP = BigDecimal.ROUND_UP;//后面只要有值,直接进位
int scale, roundingMode;
public PreciseCalculator() {
scale = 6;//默认保留6位小数,舍入方式位四舍五入
roundingMode = HALF_UP;
}
public PreciseCalculator(int scale) {
this.scale = scale;
roundingMode = HALF_UP;
}
public PreciseCalculator(int scale, int roundingMode) {
this.scale = scale;
this.roundingMode = roundingMode;
}
public BigDecimal calculate(String expression) throws IllegalOperandException, IllegalExpressionException {
if (!validate(expression))
throw new IllegalExpressionException();
String[] expr = parseExpression(expression);
return calculateBigDecimal(expr);
}
public BigDecimal calculateBigDecimal(String[] expr) throws IllegalOperandException {
Stack numStk = new Stack<>(); //操作数栈
Stack opStk = new Stack(); //操作符栈
for (String s : expr) {
if (isOperand(s)) { //遇到数字,直接压栈
BigDecimal num = new BigDecimal(s);
numStk.push(num);
} else if (s.equals("(")) { //遇到左括号,直接压栈
opStk.push(s);
} else if (s.equals(")")) {
while (true) {
String op = opStk.pop();
if (op.equals("(")) //弹出左括号,结束
break;
else { //不是左括号,则为操作符,进行运算
BigDecimal right = numStk.pop();
BigDecimal left = numStk.pop();
BigDecimal re = operateBigDecimal(left, right, op);
numStk.push(re);
}
}
} else { //+ - / *
int priority = getPriority(s);//当前操作符优先级
while (true) {
if (opStk.empty() || priority > getPriority(opStk.peek())) { //操作符栈为空 或 当前符号优先级比栈顶符号优先级高,直接入栈
opStk.push(s);
break;
} else {
BigDecimal right = numStk.pop();
BigDecimal left = numStk.pop();
String op = opStk.pop();
BigDecimal re = operateBigDecimal(left, right, op);
numStk.push(re);
}
}
}
}
while (!opStk.empty()) { //对栈中剩余数进行运算
BigDecimal right = numStk.pop();
BigDecimal left = numStk.pop();
String op = opStk.pop();
BigDecimal re = operateBigDecimal(left, right, op);
numStk.push(re);
}
return numStk.pop().setScale(scale, roundingMode);
}
}
Exceptions.java
package my.Calculator;
public class Exceptions {
public static class IllegalExpressionException extends Exception {
public IllegalExpressionException() {
super();
}
}
public static class IllegalOperandException extends Exception {
public IllegalOperandException(String msg) {
super(msg);
}
}
}