java 开源 表达式计算_Java表达式计算实现

在做URule规则引擎PRO版时,遇到表达式计算问题,就是用户随意输入一个包含+、-、*、/以及()的字符串表达式时,引擎要能根据表达式计算出结果,如果表达式中包含普通字符串,则以字符串连接方式处理。

一开始想到用apache的jexl实现,用了之后发现jexl性能实在太差,所以就自己写了一个,利用下面这个表达式计算类,可实现类似下面这些表达式的计算:

表达式

计算结果

2+20*3-10

52

2+20*3-10+superman

52superman

2+20*3-10+super man

52superman

2+20*3-10+"super man"

52super man

(2+20)*3-10+"super man"

56super man

2+20%3-10

-6

2+20/3-10

-1.3333333333

2+18/3-10

-2

2+18%3-10

-8

"super man:"+(30-20)

super man10

算法实现源码如下:

import java.math.BigDecimal;

import java.math.RoundingMode;

import java.util.Stack;

import com.bstek.urule.Utils;

/**

* @author Jacky.gao

* @since 2018年6月28日

*/

public class ElCompute {

private Stack dataStack=new Stack();

private Stack operateStack=new Stack();

public static void main(String[] args) {

long start=System.currentTimeMillis();

String expr="21%3+\"ga ojie \"+\" is \"+superman";

for(int i=0;i<1;i++) {

ElCompute el=new ElCompute();

Object data=el.doCompute(expr);

System.out.println(data);

}

long end=System.currentTimeMillis();

System.out.println(end-start);

}

public Object doCompute(String expr) {

init(expr);

return dataStack.pop();

}

private void init(String expr){

StringBuilder dataSb=new StringBuilder();

char prevQuote='0';

for(int i=0;i

char c=expr.charAt(i);

switch(c){

case '+':

addDataStack(dataSb);

doCalculate(0);

operateStack.push(c);

break;

case '-':

doMinus(dataSb, prevQuote);

break;

case '*':

addDataStack(dataSb);

doCalculate(2);

operateStack.push(c);

break;

case '/':

addDataStack(dataSb);

doCalculate(2);

operateStack.push(c);

break;

case '%':

addDataStack(dataSb);

doCalculate(2);

operateStack.push(c);

break;

case '(':

operateStack.push(c);

break;

case ')':

addDataStack(dataSb);

doCalculate(1);

break;

case '"':

if(prevQuote=='"'){

prevQuote='0';

dataStack.push(dataSb.toString());

dataSb.setLength(0);

}else{

prevQuote='"';

}

break;

case ' ':

if(prevQuote=='"') {

dataSb.append(c);

}

break;

default:

dataSb.append(c);

}

}

if(dataSb.length()>0){

addDataStack(dataSb);

}

doCalculate(0);

}

private void doMinus(StringBuilder dataSb,char prevQuote){

if(dataSb.length()==0){

dataSb.append('-');

}else{

addDataStack(dataSb);

doCalculate(0);

operateStack.push('-');

}

}

private void doCalculate(int current) {

if(operateStack.empty()){

return;

}

char prevOp=operateStack.peek();

if(prevOp=='('){

return;

}

if(current==0 || current==1){

char op=operateStack.pop();

do{

Object right=dataStack.pop();

Object left=dataStack.pop();

Object result=calculate(left, op, right);

dataStack.push(result);

if(operateStack.isEmpty()){

break;

}

op=operateStack.pop();

}while(op!='(');

}else if(current==2){

while(prevOp=='*' || prevOp=='/' || prevOp=='%') {

Object right=dataStack.pop();

Object left=dataStack.pop();

char op=operateStack.pop();

Object result=calculate(left, op, right);

dataStack.push(result);

if(operateStack.isEmpty()){

break;

}

prevOp=operateStack.peek();

if(prevOp=='('){

break;

}

}

}

}

private Object calculate(Object left,char op,Object right){

if((op=='*' || op=='/' || op=='%' || op=='-')){

if(right instanceof String || left instanceof String){

throw new RuntimeException(left + " and "+right+" can't do "+op+"!");

}

BigDecimal b1=(BigDecimal)left;

BigDecimal b2=(BigDecimal)right;

if(op=='*'){

return b1.multiply(b2);

}else if(op=='/'){

return b1.divide(b2,10,RoundingMode.HALF_UP).stripTrailingZeros();

}else if(op=='%'){

return b1.divideAndRemainder(b2)[1];

}else if(op=='-'){

return b1.subtract(b2);

}

}else if(op=='+'){

if(right instanceof String || left instanceof String){

return left.toString()+right.toString();

}else{

BigDecimal b1=(BigDecimal)left;

BigDecimal b2=(BigDecimal)right;

return b1.add(b2);

}

}

throw new RuntimeException("Unkown operate "+op+"");

}

private void addDataStack(StringBuilder dataSb) {

if(dataSb.length()==0)return;

String data=dataSb.toString();

dataSb.setLength(0);

try{

BigDecimal bd=Utils.toBigDecimal(data);

dataStack.push(bd);

}catch(Exception ex){

dataStack.push(data);

}

}

}

在上面的代码中Utils.toBigDecimal(data)的作用是尝试将当前数据转换为BigDecimal,Utils.toBigDecimal方法内容如下:

public static BigDecimal toBigDecimal(Object val) {

try{

if (val instanceof BigDecimal) {

return (BigDecimal) val;

} else if (val == null) {

throw new IllegalArgumentException("Null can not to BigDecimal.");

} else if (val instanceof String) {

String str = (String) val;

if ("".equals(str.trim())) {

return BigDecimal.valueOf(0);

}

str=str.trim();

return new BigDecimal(str);

} else if (val instanceof Number) {

return new BigDecimal(val.toString());

} else if (val instanceof Character) {

int i = ((Character) val).charValue();

return new BigDecimal(i);

}

}catch(Exception ex){

throw new NumberFormatException("Can not convert "+val+" to number.");

}

throw new IllegalArgumentException(val.getClass().getName()+" can not to BigDecimal.");

}

如果您的项目中要用到字符串表达式计算,那么可以直接用上述方法实现,目前这个算法已应用在URule PRO版以及UReport2的当中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值