四则运算工具类,支持+-*/()开根号

新代码对计算方式做了优化,最近有新需求,需要加上根号处理,故对代码进行了调整

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.regex.Pattern;


@Slf4j
public class CalculatorFormulaNewUtil {
    private static final String NUMBER_REGEX = "[0-9]";
    /**
     * 中缀变后缀
     */
    public static List<String> changeInfixExpressionToPostfixExpression(String input){
        List<String> resultList=new ArrayList<>();
        Stack<String> tempStack= new Stack<>();
        // T替换回sqrt
        String[] splitArray = input.split(" ");
        //去除字符串数组中的空串
        splitArray = delNilString(splitArray);

        for (int i=0;i<splitArray.length;i++){
            if (")".equals(splitArray[i])){
                while (!"(".equals(tempStack.peek())){
                    resultList.add(tempStack.pop());
                }
                // 如果去除左括号之后 前一个是开方的话 直接加入
                if ("T".equals(tempStack.peek())){
                    resultList.add(tempStack.pop());
                }
                tempStack.pop();//去除前面的左括号
            }else if ("(".equals(splitArray[i]) || "T".equals(splitArray[i])){
                //如果是左括号,那么直接添加进去
                // 如果是开方sqrt 说明需要直接对前一个计算结果进行开方 先进行压栈 因为需要最后的结果才进行开方
                tempStack.add(splitArray[i]);
            }else if ("+".equals(splitArray[i])|| "-".equals(splitArray[i])){
                if (tempStack.empty()|| "(".equals(tempStack.peek())){
                    tempStack.add(splitArray[i]);
                }else if ("+".equals(tempStack.peek())|| "-".equals(tempStack.peek()) || "T".equals(tempStack.peek())){
                    //读临时栈里的顶部数据,如果也是加减就取出来一个到结果列,这个放临时栈,如果是乘除就开始取到右括号为止
                    resultList.add(tempStack.pop());
                    tempStack.add(splitArray[i]);
                }else {
                    while (!tempStack.empty()){
                        if ("(".equals(tempStack.peek())){
                            break;
                        }else {
                            resultList.add(tempStack.pop());
                        }
                    }
                    tempStack.add(splitArray[i]);
                }
            }else if ("*".equals(splitArray[i])|| "/".equals(splitArray[i])){
                //如果是乘除
                if (!tempStack.empty()){
                    //判断临时栈里栈顶是啥,如果是乘除,取一个出来到结果列,添这个进临时栈
                    if ("*".equals(tempStack.peek())|| "/".equals(tempStack.peek()) || "T".equals(tempStack.peek())){
                        resultList.add(tempStack.pop());
                    }
                }
                tempStack.add(splitArray[i]);
            } else {
                //说明是非符号,都添加进去
                resultList.add(splitArray[i]);
            }
        }
        //遍历完了,把临时stack里的东西都加到结果stack里去
        while (!tempStack.empty()){
            resultList.add(tempStack.pop());
        }
        return resultList;
    }

    /**
     * 去除空串
     */
    public static String[] delNilString(String[] arr){
        //用StringBuffer来存放数组中的非空元素,用“;”分隔
        StringBuilder sb = new StringBuilder();
        for(int i=0; i<arr.length; i++) {
            if("".equals(arr[i])) {
                continue;
            }
            sb.append(arr[i]);
            if(i != arr.length - 1) {
                sb.append(";");
            }
        }
        //用String的split方法分割,得到数组
        arr = sb.toString().split(";");
        return arr;
    }

    /**
     * 获取结果
     */
    public static BigDecimal getResult(String input){
        if (StrUtil.isEmpty(input)){
            log.debug("formula is null, formula = {}", input);
            return null;
        }
        //规范输入形式,避免用户输入中文括号
        input = input.toLowerCase().replaceAll("(","(");
        input = input.replaceAll(")",")");
        // sqrt用T代替 避免后面不好split
        input = input.replaceAll("sqrt", "T");
        //对输入公式,按符号/数字,用空格分开,以便后面分组
        String[] inputs = input.split("");
        StringBuilder format= new StringBuilder();
        for (String s : inputs) {
            if ("(".equals(s) || ")".equals(s) || "+".equals(s) || "-".equals(s) || "*".equals(s) || "/".equals(s)
             || "T".equals(s)) {
                format.append(" ").append(s).append(" ");
            } else {
                format.append(s);
            }
        }
        List<String> strings = changeInfixExpressionToPostfixExpression(format.toString());
        Stack<String> stack= new Stack<>();
        try {
            for (String string : strings) {
                if ("+".equals(string)) {
                    BigDecimal a = new BigDecimal(stack.pop());
                    BigDecimal b = new BigDecimal(stack.pop());
                    stack.add(b.add(a).toString());
                } else if ("-".equals(string)) {
                    BigDecimal a = new BigDecimal(stack.pop());
                    BigDecimal b = new BigDecimal(stack.pop());
                    stack.add(b.subtract(a).toString());
                } else if ("*".equals(string)) {
                    BigDecimal a = new BigDecimal(stack.pop());
                    BigDecimal b = new BigDecimal(stack.pop());
                    stack.add(b.multiply(a).toString());
                } else if ("/".equals(string)) {
                    BigDecimal a = new BigDecimal(stack.pop());
                    BigDecimal b = new BigDecimal(stack.pop());
                    //这里的1000是做除法以后计算的精确位数,就算1000位也并不会拖慢程序速度,一个公式0.01秒内就能算完,后面的是除不尽的四舍五入
                    stack.add(b.divide(a, 1000, BigDecimal.ROUND_HALF_DOWN).toString());
                } else if (string.contains(".")){
                    // 需要判断是否是个数字
                    // 去除括号校验一下
                    String checkString = string.replaceAll("[()]","");
                    boolean number = NumberUtil.isNumber(checkString);
                    if (!number){
                        log.debug("formula error... formula = {}", input);
                        return null;
                    }
                    // 要求.之前和.之后必须是数字0-9 防止出现.n n.这种情况
                    String firstString = checkString.substring(0, 1);
                    String lastString = checkString.substring(checkString.length() -1);
                    if(!Pattern.matches(NUMBER_REGEX, firstString)
                            || !Pattern.matches(NUMBER_REGEX, lastString)){
                        log.debug("formula error... formula = {}", input);
                        return null;
                    }


                    stack.add(string);
                } else if ("T".equals(string)){
                    // 去除栈中第一个数开方再塞回去
                    BigDecimal a = new BigDecimal(stack.pop());
                    stack.add(BigDecimal.valueOf(Math.sqrt(a.doubleValue()))
                        .setScale(1000, RoundingMode.HALF_UP).toPlainString());
                } else {
                    // 到这只可能是数字
                    stack.add(string);
                }
            }
            // 上面的算完 按理说stack里面只能有一个值
            // 如果出现两个 这说明存在两个数字相连但是没有运算符的情况
            if (stack.size() > 1){
                log.debug("formula error... formula = {}", input);
                return null;
            }
            //返回的时候格式化一下,取四舍五入小数点后两位
            return new BigDecimal(stack.pop()).setScale(4, RoundingMode.HALF_UP);
        } catch (Exception e){
            // 公式有误
            log.debug("formula error ... formula = {}", input);
            return null;
        }
    }

//    public static void main(String[] args) {
//        System.out.println(getResult("2 + 3 + Sqrt1+ (1+ 2 + 2 * sQrt(2+14) * 3) + SQRT(5*5)"));
//    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

扫地情圣!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值