Java实现常用加减乘除公式解析及计算-1.0

当前计算器工具不够完善且有计算bug,请移步Java实现常用加减乘除公式解析及计算-2.0查看最新实现~

近期接到一个实现公式解析计算、能够多层嵌套并自动拆解的需求。即对于给定的字符串公式进行解析及计算,实现从持久层自动装载公式,拆解多层嵌套的公式,得到计算结果。大致的设计思路为:定义一个计算器基类,实现基本属性的装载及加减乘除公式的定义,实现公式拆解、运算符分级及解析计算;定义子类实现不同的装载逻辑和公式解析逻辑。

基类:

package cn.ac.sict.calculator;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * 计算器抽象类定义
 * @author yjj
 * 2022-07-01 13:48
 */
public abstract class Calculator {
    //公式id
    private String id;
    //公式名称
    private String name;
    //公式内容
    private String formula;
    //公式类型,true代表formula为数值,false代表formula为计算公式
    private boolean type = false;
    //计算精度
    public static int precision = 0;
    //默认值
    public static String defaultValue = "0";
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getFormula() {
        return formula;
    }
    public void setFormula(String formula) {
        //把公式中的负数都转化为0-N的形式,方便计算
        if(isValidString(formula)) {
            StringBuffer stringBuffer = new StringBuffer();
            //匹配公式开头的负数部分或中间的负数部分,即以-开头或-前为其他运算符号
            Matcher matcher = Pattern.compile("(^-\\d+\\.?\\d*)|([\\(\\+\\-\\*\\/]{1}-\\d+\\.?\\d*)").matcher(formula);
            int start = 0;
            while(matcher.find()) {
                String group = matcher.group();
                String replacement;
                if(group.charAt(1) >= '0' && group.charAt(1) <= '9') {
                    replacement = "(0" + group + ")";
                } else {
                    replacement = group.substring(0, 1) + "(0" + group.substring(1) + ")";
                }
                stringBuffer.append(formula, start, matcher.start()).append(replacement);
                start = matcher.end();
            }
            stringBuffer.append(formula, start, formula.length());
            this.formula = stringBuffer.toString();
        } else {
            this.formula = defaultValue;
        }
    }
    public boolean isType() {
        return type;
    }
    public void setType(boolean type) {
        this.type = type;
    }
    protected Calculator() {
    }
    /**
     * 是否有效的String
     * @param string
     * @return boolean
     */
    private static boolean isValidString(String string) {
        return string != null && !"".equals(string) && !"null".equalsIgnoreCase(string);
    }
    /**
     * 是否有效的数值
     * @param value
     * @return boolean
     */
    private static boolean isValidDigit(String value) {
        if(isValidString(value)) {
            value = value.trim();
            return Pattern.compile("^(\\+|-)?\\d+($|\\.\\d+$)").matcher(value).matches();
        } else {
            return false;
        }
    }
    /**
     * 数值格式化
     * @param value 数值字符串
     * @return String
     */
    private static String format(String value) {
        StringBuffer expression = new StringBuffer("0");
        if(precision > 0) {
            expression.append(".");
            for(int i=0;i<precision;i++) {
                expression.append("0");
            }
        }
        DecimalFormat decimalFormat = new DecimalFormat(expression.toString());
        return decimalFormat.format(new BigDecimal(validate(value)));
    }
    /**
     * 数值有效化
     * @param value 数值字符串
     * @return String
     */
    private static String validate(String value) {
        if(isValidDigit(value)) {
            return value.trim();
        } else {
            return defaultValue;
        }
    }
    /**
     * 加
     * @param left 左操作数
     * @param right 右操作数
     * @return String
     */
    private static String add(String left, String right) {
        BigDecimal arg1 = new BigDecimal(validate(left));
        BigDecimal arg2 = new BigDecimal(validate(right));
        BigDecimal arg3 = arg1.add(arg2);
        return format(arg3.toString());
    }
    /**
     * 减
     * @param left 左操作数
     * @param right 右操作数
     * @return String
     */
    private static String subtract(String left, String right) {
        BigDecimal arg1 = new BigDecimal(validate(left));
        BigDecimal arg2 = new BigDecimal(validate(right));
        BigDecimal arg3 = arg1.subtract(arg2);
        return format(arg3.toString());
    }
    /**
     * 乘
     * @param left 左操作数
     * @param right 右操作数
     * @return String
     */
    private static String multiply(String left, String right) {
        BigDecimal arg1 = new BigDecimal(validate(left));
        BigDecimal arg2 = new BigDecimal(validate(right));
        BigDecimal arg3 = arg1.multiply(arg2);
        return format(arg3.toString());
    }
    /**
     * 除
     * @param left 左操作数
     * @param right 右操作数
     * @return String
     */
    private static String divide(String left, String right) {
        BigDecimal arg1 = new BigDecimal(validate(left));
        BigDecimal arg2 = new BigDecimal(validate(right));
        if(arg2.compareTo(BigDecimal.ZERO) == 0) {
            return defaultValue;
        }
        BigDecimal arg3 = arg1.divide(arg2, precision, RoundingMode.HALF_EVEN);
        return format(arg3.toString());
    }
    /**
     * 获取操作符的优先级
     * @param operator
     * @return int
     */
    private static int getPriority(String operator) {
        if("(".equals(operator)) {
            return -1;
        }
        if(")".equals(operator)) {
            return 0;
        }
        if("+".equals(operator) || "-".equals(operator)) {
            return 1;
        }
        if("*".equals(operator) || "/".equals(operator)) {
            return 2;
        }
        return -2;
    }
    /**
     * 计算
     * @param left
     * @param right
     * @param operator
     * @return String
     */
    private static String calculate(String left, String right, String operator) {
        if("+".equals(operator)) {
            return add(left, right);
        }
        if("-".equals(operator)) {
            return subtract(left, right);
        }
        if("*".equals(operator)) {
            return multiply(left, right);
        }
        if("/".equals(operator)) {
            return divide(left, right);
        }
        return defaultValue;
    }
    /**
     * 拆分公式
     * @return List
     */
    protected List<String> splitFormula() {
        int length = formula.length();
        List<String> result = new ArrayList<>(length);
        StringBuffer item = new StringBuffer();
        for(int i=0;i<length;i++) {
            char c = formula.charAt(i);
            if(c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/') {
                if(item.length() > 0) {
                    result.add(item.toString());
                    item.setLength(0);
                }
                item.append(c);
                result.add(item.toString());
                item.setLength(0);
            } else if((c >= '0' && c <= '9') || c == '#' || c == '.') {
                item.append(c);
            }
        }
        if(item.length() > 0) {
            result.add(item.toString());
        }
        return result;
    }
    /**
     * 抽象的初始化方法,由子类实现
     */
    public abstract void init();
    /**
     * 抽象的解析公式方法,由子类实现
     * @return List
     */
    public abstract List<String> parseFormula();
    /**
     * 公式计算方法
     * @param list 解析后的公式
     * @return String
     */
    public static String calculate(List<String> list) {
        if(list == null || list.size() == 0) {
            return defaultValue;
        }
        Stack<String> operatorStack = new Stack<>();
        Stack<String> numberStack = new Stack<>();
        int size = list.size();
        for(int i=0;i<size;i++) {
            String str = list.get(i);
            if("(".equals(str)) {
                operatorStack.push(str);
            } else if(isValidDigit(str)) {
                numberStack.push(str);
            } else if("+".equals(str) || "-".equals(str) || "*".equals(str) || "/".equals(str) || ")".equals(str)) {
                while(numberStack.size() >= 2 && operatorStack.size() >= 1 && getPriority(str) <= getPriority(operatorStack.peek())) {
                    String operator = operatorStack.pop();
                    String right = numberStack.pop();
                    String left = numberStack.pop();
                    numberStack.push(calculate(left, right, operator));
                }
                operatorStack.push(str);
                if(")".equals(str)) {
                    while(!"(".equals(operatorStack.peek())) {
                        operatorStack.pop();
                    }
                    operatorStack.pop();
                }
            }
        }
        while(numberStack.size() >= 2 && operatorStack.size() >= 1) {
            String operator = operatorStack.pop();
            String right = numberStack.pop();
            String left = numberStack.pop();
            numberStack.push(calculate(left, right, operator));
        }
        return numberStack.pop();
    }
}

解析json格式数据的子类:

package cn.ac.sict.calculator;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
 * 计算器json方式实现类
 * @author yjj
 * 2022-06-30 13:47
 */
public class CalculatorJsonImpl extends Calculator {
    public String filePath = "D:\\ide\\project\\Calculator\\cn\\ac\\sict\\calculator\\data.json";
    private CalculatorJsonImpl() {
        super();
    }
    /**
     * 创建实例
     * @param id
     * @return CalculatorJsonImpl
     */
    public static CalculatorJsonImpl create(String id) {
        CalculatorJsonImpl result = new CalculatorJsonImpl();
        result.setId(id);
        return result;
    }
    /**
     * 初始化方法
     */
    @Override
    public void init() {
        JSONArray jsonArray = JSONUtil.readJSONArray(new File(filePath), CharsetUtil.charset("UTF-8"));
        jsonArray.stream().forEach(obj -> {
            JSONObject jsonObject = JSONUtil.parseObj(obj);
            if(getId().equals(jsonObject.getStr("id"))) {
                setName(jsonObject.getStr("name"));
                setFormula(jsonObject.getStr("formula"));
                setType(jsonObject.getBool("type"));
            }
        });
    }
    /**
     * 解析公式
     * @return List
     */
    @Override
    public List<String> parseFormula() {
        List<String> result = new ArrayList<>();
        if(isType()) {
            result.add(getFormula());
            return result;
        }
        List<String> list = splitFormula();
        for(String str : list) {
            if(str.startsWith("#")) {
                String id = str.substring(1);
                CalculatorJsonImpl sub = CalculatorJsonImpl.create(id);
                sub.init();
                result.addAll(sub.parseFormula());
            } else {
                result.add(str);
            }
        }
        return result;
    }
}

在实际项目中可以把子类定义为从数据库获取公式定义,方便管理。

测试数据:

[
  {
    "id": "1",
    "name": "测试1",
    "formula": "1+2",
    "type": false
  },
  {
    "id": "2",
    "name": "测试2",
    "formula": "#1+3",
    "type": false
  },
  {
    "id": "3",
    "name": "测试3",
    "formula": "#2*-1*#4",
    "type": false
  },
  {
    "id": "4",
    "name": "测试4",
    "formula": "5",
    "type": true
  },
  {
    "id": "5",
    "name": "测试5",
    "formula": "2/(3.6+((5-100)+50.3))*(3-1)+6-8",
    "type": false
  }
]

测试类:

package cn.ac.sict.calculator;
import java.util.List;
/**
 * @author yjj
 * 2022-07-01 15:59
 */
public class Test {
    public static void main(String[] args) {
        
        Calculator calculator = CalculatorJsonImpl.create("3");
        calculator.init();
        System.out.println(calculator.getName());
        List<String> list = calculator.parseFormula();
        System.out.println("解析后的公式为:" + list);
        String result = Calculator.calculate(list);
        System.out.println("计算结果为:" + result);

        calculator = CalculatorJsonImpl.create("5");
        calculator.init();
        System.out.println(calculator.getName());
        list = calculator.parseFormula();
        System.out.println("解析后的公式为:" + list);
        Calculator.precision = 2;
        result = Calculator.calculate(list);
        System.out.println("计算结果为:" + result);
    }
}

测试结果:

测试3

解析后的公式为:[1, +, 2, +, 3, *, (, 0, -, 1, ), *, 5]
计算结果为:-12

测试5

解析后的公式为:[2, /, (, 3.6, +, (, (, 5, -, 100, ), +, 50.3, ), ), *, (, 3, -, 1, ), +, 6, -, 8]
计算结果为:-2.10

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值