前面的几个public的方法返回计算结果。这里引入了两个异常类,可以替换成其他的。
实际上可以使用java中的js引擎直接计算(将字符串当作js语句执行),代码非常简洁可靠,但可能效率低些,如果涉及用户输入还有安全问题。
package org.utils;
import org.apache.commons.beanutils.ConversionException;
import org.springframework.expression.ExpressionException;
/**
* 运算工具类,解析字符串运算式,
* @author 一只拖拉机
* @date 2021年9月30日 上午9:44:22
*/
public final class CalculationUtils {
/**
* 表达式必须是boolean类型的,否则报错
* 支持的比较符号 == >= > <= <
* @param calculationString
* @return
* @version
* @throws Exception
*/
public static Boolean getBoolean(String calculationString) throws Exception {
calculationString = format(calculationString);
Object res = getObject(calculationString);
if(res != null && res instanceof Boolean)return (Boolean)res;
return null;
}
public static Double getDouble(String calculationString) throws Exception {
calculationString = format(calculationString);
Object res = getObject(calculationString);
if(res != null && res instanceof Double)return (Double)res;
return null;
}
public static Double getDouble(String calculationString,int scale) throws Exception {
calculationString = format(calculationString);
Object res = getObject(calculationString);
if(res != null && res instanceof Double)return round((Double)res,scale);
return null;
}
/**
* 根据表达式类型,返回Double或Boolean
* @param calculationString
* @return
* @version
* @throws Exception
*/
public static Object getResult(String calculationString) throws Exception {
calculationString = format(calculationString);
return getObject(calculationString);
}
public static Integer getInt(String calculationString) throws Exception {
Object res;
calculationString = format(calculationString);
res = getObject(calculationString);
if(res != null && res instanceof Double)return ((Double)res).intValue();
return null;
}
public static double round(double v, int scale) {
if (scale < 0 || scale > 10) {
throw new IllegalArgumentException("scale参数必须介于[0,10]");
}
java.math.BigDecimal b = new java.math.BigDecimal(Double.toString(v));
java.math.BigDecimal one = new java.math.BigDecimal("1");
return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* 格式预处理,去空格,回车等
* @param calculationString
* @return
* @version
*/
private static String format(String calculationString) {
return calculationString.replaceAll("\\s*|\\t|\\r|\\n", "");
}
private static Object getObject(String calculationString ) throws Exception {
check(calculationString);
return getObject1(calculationString,false);
}
/**
* 可能返回的类型,boolean,double,string
* <p>递归去括号
* @param calculationString
* @return
* @throws Exception
* @version
*/
private static Object getObject1(String calculationString,boolean isFirst) throws Exception{
int a = calculationString.indexOf("(");
if(a > -1) {
String p1 = calculationString.substring(0,a);
Object p2 = getObject1(calculationString.substring(a+1),true); // 递归字符串,该字符串去除第一个(,下一层会去掉对应的)
if(p1.length()==0 && !(p2 instanceof String))return p2;
String newstr = p1+p2;
if( (a = newstr.indexOf(")")) > -1 ) { // 递归结果还有(,表示还有括号可以递归
return getObject1(newstr,true);
}else { // 递归后没有括号了,直接返回计算结果
return getSingleObject(newstr);
}
}else { // 获得并计算优先级最高的括号内容(里面没有括号了)
int i1 = calculationString.indexOf(")");
if(i1 > -1) { // 有) 时需要分割后运算,返回时去除第一个)
if(!isFirst)throw new ExpressionException("符号 ) 异常");
String h1 = calculationString.substring(0,i1);
String h2 = calculationString.substring(i1+1);
if(h2.length()>0)return getSingleObject(h1)+h2; // )后面还有算式字符串p2,运算结果拼接p2,并返回
return getSingleObject(h1); // 直接返回运算结果,类型为boolean或double。
}else {
if(isFirst)throw new ExpressionException("符号 ( 异常");
}
return getSingleObject(calculationString); // 直接返回运算结果,类型为boolean或double
}
}
/**
* 计算无括号的运算式,可能返回boolean、double
* <p>计算的表达式如:1+3-6*2/2 == 3 或 1+3-6*2/2
* @param calculationString
* @return
* @version
*/
private static Object getSingleObject(String calculationString) throws ConversionException{
int cou = 0;
String regx = null;
if(calculationString.indexOf(">=")>-1) {
cou++;regx = ">=";
}else if(calculationString.indexOf(">")>-1) {
cou++;regx = ">";
}
if(calculationString.indexOf("<=")>-1) {
cou++;regx = "<=";
}else if(calculationString.indexOf("<")>-1) {
cou++;regx = "<";
}
if(calculationString.indexOf("==")>-1) {
cou++;regx = "==";
}
switch (cou) {
case 0: // 返回数字
return computeNumber(calculationString);
case 1: // 返回boolean
String[] two = calculationString.split(regx);
if(two == null || two.length != 2) {
System.out.println("错误的格式1:"+calculationString);
return null;
}
return compareBoolean(computeNumber(two[0]),regx,computeNumber(two[1]));
default: // 错误的格式
System.out.println("错误的格式:"+calculationString);
return null;
}
}
/**
* 计算无括号算式,如 1+2*5-8 。只返回null或double。算式异常时返回null
* @param a
* @exception ExpressionException :表达式错误异常
* @exception NumberFormatException 除数0、数字转化异常
* @return
* @version
*/
private static Double computeNumber(String a) {
if(a == null)return null;
char[] chars = a.toCharArray();
Double tem = null,m1=null;
int f=0;
char ty = 0,f1=0,f2=0; // 1+ 2- 3 * 4/
for(int i=0;i<chars.length;i++) {
char t = chars[i];
if(t<42 || t== 44 || t > 57)throw new ExpressionException("出现了不应该出现的字符:"+t);; // 只允许的符号 0至9 + - . / *
if(t < 48 && t != 46 && i > 0 && chars[i-1] > 47 && chars[i-1] < 58) { // + - * /
if(t=='-' || t=='+') {
if(m1 != null) {
Double ddd= Double.valueOf(String.valueOf(chars, f, i-f));
if(f1 > 0) {
tem = computeNormal(f1,tem,computeNormal(f2,m1,ddd));
}else {
tem = computeNormal(f2,m1,ddd);
}
m1 = null;
}else if(f1=='-') {
tem =tem - Double.valueOf(String.valueOf(chars, f, i-f));
}else if (f1 == '+') {
tem = tem + Double.valueOf(String.valueOf(chars, f, i-f));
}else { // 首次
tem = Double.valueOf(String.valueOf(chars, f, i-f));
}
f1 = t;
}else {
if(m1 != null) {
m1 = computeNormal(f2,m1,Double.valueOf(String.valueOf(chars, f, i-f)));
}else {
m1 = Double.valueOf(String.valueOf(chars, f, i-f));
}
f2 = t;
}
ty = t;
f = i + 1;
}
if(i == chars.length-1) {
if(i-f+1 == 0) throw new ExpressionException("错误的表达式:"+chars[i]); // 表示最后一个是字符
Double ddd= Double.valueOf(String.valueOf(chars, f, i-f+1));
if(f == 0)return ddd;
if(m1 != null) {
if(f1 > 0) {
return computeNormal(f1,tem,computeNormal(f2,m1,ddd));
}
return computeNormal(f2,m1,ddd);
}
return computeNormal(ty,tem,ddd);
}
}
throw new ExpressionException("错误的表达式");
}
/**
* 检查括号
* @param expr
* @return
* @version
*/
private static void check(String expr) {
int a=0,b=0;
for(int i=0; i< expr.length();i++) {
if('(' == expr.charAt(i)) {
a ++;
}else if(')' == expr.charAt(i)) {
b ++;
}
}
if(a != b)throw new ExpressionException("括号 异常");
}
/**
* 比较两个double数据大小,需要传入字符型的 “==” , ">=" , ">" 等
* @param v1
* @param tag
* @param v2
* @return
* @version
*/
private static Boolean compareBoolean(Double v1,String tag,Double v2) {
if("==".equals(tag)) {
return v1.equals(v2);
}else if(">=".equals(tag)) {
return v1 >= v2;
}else if("<=".equals(tag)) {
return v1 <= v2;
}else if(">".equals(tag)) {
return v1 > v2;
}else if("<".equals(tag)) {
return v1 < v2;
}else {
return false;
}
}
private static Double computeNormal(char sign,Double d1,Double d2) {
if(d1==null || d2== null)return null;
switch(sign) {
case '-':
return d1 - d2;
case '+':
return d1 + d2;
case '*':
return d1 * d2;
case '/':
if(d2 == 0)throw new NumberFormatException("除数不能为0");
return d1 / d2;
default :
return null;
}
}
}