字符串公式计算方法

说明

其实这个应用场景并不多见,但碍于方法以及代码优秀,网络上也没有出现比较完美的解决方案,于是乎再一次从json-script-rule中扒下来一段代码,分享给大家,有需要的尽管拿去用吧!
提示1:老规矩,先认真读注释说明,看使用的例子,该方法不会造成精度问题,且性能测试良好,采用双链表结构加递归处理巧妙解决问题
提示2:java8以上的版本存在一些新的语法如getFirst(),如果你的版本是8那么可以大胆的替换成get(0)
提示3:calculate有多个重载方法,注意方法中调用的是递归calculate还是下面抽离出去的calculate

/**
     * <p>计算字符串中的公式,公式必须是合理的公式,不能有除运算符外的其它字符包括空格
     * <p>例子:calculate("-3.3*((-1.2+0.2)*(10%(-3.2/10)))"),结果:0.2640(默认四舍五入),程序计算结果:0.2639999999999993
     * @param expression 计算公式的表达式
     * @param scale 用于除法计算时保留的小数位
     * @param mode 选择舍入模式,默认四舍五入
     * */
    public static BigDecimal calculate(String expression,int scale, RoundingMode mode){
        try{
            if (isBlank(expression)){
                throw new JSRuleException();
            }
            while(expression.contains(ZSSign.RIGHT_BRACKET_STR)){
                StringBuilder equation = new StringBuilder(expression);
                int end = equation.indexOf(ZSSign.RIGHT_BRACKET_STR);
                String sub =  equation.substring(0,end);
                int start = sub.lastIndexOf(ZSSign.LEFT_BRACKET_STR);
                expression = equation.replace(start, end+1,calculate(sub.substring(start+1), scale, mode).toPlainString()).toString();
            }
            LinkedList<BigDecimal> value = new LinkedList<>();
            LinkedList<Character> symbol = new LinkedList<>();
            StringBuilder number = new StringBuilder();
            char[] cs = expression.toCharArray();
            if (cs[0]=='-'){
                number.append(0);
            }
            for (char c : cs) {
                if (Character.isDigit(c) || c == '.') {
                    number.append(c);
                } else {
                    if (c=='-' && number.isEmpty()){
                        number.append(c);
                    }else{
                        calculate(value,number.toString(),symbol,scale,mode);
                        number.setLength(0);
                        symbol.addLast(c);
                    }
                }
            }
            calculate(value,number.toString(),symbol,scale,mode);
            BigDecimal result = value.getFirst();
            for (int i=0;i<symbol.size();i++){
                result = calculate(result,symbol.get(i),value.get(i+1),scale,mode);
            }
            return result;
        }catch (Exception e){
            throw new JSRuleException(JSRuleMessage.read("illegal.expression"));
        }
    }
private static void calculate(LinkedList<BigDecimal> value,String number,LinkedList<Character> symbol,int scale, RoundingMode mode){
        if (!symbol.isEmpty()) {
            char lastSymbol = symbol.getLast();
            if (lastSymbol == '+' || lastSymbol == '-') {
                value.addLast(new BigDecimal(number));
            } else {
                value.addLast(calculate(value.removeLast(), symbol.removeLast(), new BigDecimal(number),scale,mode));
            }
        } else {
            value.addLast(new BigDecimal(number));
        }
    }
public static BigDecimal calculate(BigDecimal left, char operation, BigDecimal right, int scale, RoundingMode mode){
        if (operation== '+'){
            return left.add(right);
        }else if (operation=='-'){
            return left.subtract(right);
        }else if (operation=='*'){
            return left.multiply(right);
        }else if (operation=='/'){
            return left.divide(right,scale,mode);
        }else if (operation=='%'){
            return left.divideAndRemainder(right)[1];
        }
        throw new JSRuleException(JSRuleMessage.read("unsupported.arithmetic.type"),String.valueOf(operation));
    }

使用该方法可以用()括号,也可以是负数开头,只要你的表达式正确,程序能够处理的它也可以处理,唯一不同的是如果你写成这样,3.1(2+2),它也是允许的,结果为3.14

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容