解析字符串公式

解析字符串公式

如何将字符串转换为数学公式?

1. JEP

  1. **JEP (Java Math Expression Parser)**库:JEP 是一个功能强大的Java库,用于解析和计算数学表达式。它支持变量、函数、常量等,并且可以处理各种数学运算。你可以通过 JEP 将字符串转换为数学表达式,然后计算其值。
    依赖引入:
   <dependency>
            <groupId>org.scijava</groupId>
            <artifactId>jep</artifactId>
            <version>2.4.2</version>
        </dependency>
 public static BigDecimal calculate(String formula, BigDecimal tilt) {
        // 创建一个 JEP 对象
        JEP jep = new JEP();

        // 设置变量 "tilt" 的值
        jep.addVariable("tilt", tilt.doubleValue());

        // 解析表达式
        jep.parseExpression(formula);

        // 计算结果
        BigDecimal resultBigDecimal = BigDecimal.valueOf(jep.getValue());

        // 设置小数位数和舍入规则
        return resultBigDecimal.setScale(8, RoundingMode.HALF_UP);
    }

  1. Apache Commons Math:Apache Commons Math库也提供了一些数学表达式解析的功能。它支持不同的数学函数和运算,可以将字符串转换为数学表达式并进行计算。

    Apache Commons Math库的使用示例:

    import org.apache.commons.math3.analysis.function.Sin;
    import org.apache.commons.math3.analysis.function.Add;
    
    public class MathParser {
        public static void main(String[] args) {
            String expression = "2*x + sin(x)";
            Add function = new Add(new Mult(2.0, new Identity()), new Sin());
            UnivariateFunction ufunc = function.compile(expression);
            double result = ufunc.value(10.0);
    
            System.out.println("Result: " + result);
        }
    }
    
  2. 还有javaScript库等一些方法,但是这些都需要引入依赖。

2. 无需引入依赖,可匹配例如-0.00000057e-12*tilt*tilt+0.00178274*tilt+0.95884259的公式

整个代码为:

public static BigDecimal calculate(String formula, BigDecimal tilt) {

        //先将科学计数法转换为非科学计数法:
        formula = convertToPlainFormula(formula);

        // 去掉空格, 替换公式中的 "tilt"
        formula = formula.replaceAll(" ", "").replaceAll("tilt", tilt.toString());

        String[] tokens = formula.split("(?<=[-+*/])|(?=[-+*/])");
        Stack<BigDecimal> numbers = new Stack<>();
        Stack<Character> operators = new Stack<>();
        boolean isNegative = false; // 用于处理负数开头的情况

        for (String token : tokens) {
            if (token.matches("[+\\-*/]")) {
                if (token.equals("-")) {
                    if (isNegative) {
                        isNegative = false;
                        BigDecimal number = numbers.pop();
                        numbers.push(number.negate()); // 处理负数开头的情况
                    } else {
                        isNegative = true;
                    }
                } else {
                    char operator = token.charAt(0);
                    while (!operators.isEmpty() && hasPrecedence(operator, operators.peek())) {
                        BigDecimal b = numbers.pop();
                        BigDecimal a = numbers.pop();
                        char op = operators.pop();
                        BigDecimal result = performOperation(a, b, op);
                        numbers.push(result);
                    }
                    operators.push(operator);
                }
            } else {
                BigDecimal number = new BigDecimal(token);
                if (isNegative) {
                    number = number.negate();
                    isNegative = false;
                }
                numbers.push(number);
            }
        }

        while (!operators.isEmpty()) {
            BigDecimal b = numbers.pop();
            BigDecimal a = numbers.pop();
            char op = operators.pop();
            BigDecimal result = performOperation(a, b, op);
            numbers.push(result);
        }

        return numbers.pop();
    }

    @NotNull
    private static String convertToPlainFormula(String formula) {
        // 使用正则表达式匹配科学计数法表示的数字
        Pattern pattern = Pattern.compile("([+-]?\\d*\\.?\\d*[eE][+-]?\\d+)");
        Matcher matcher = pattern.matcher(formula);

        // 逐个替换匹配到的科学计数法表示
        while (matcher.find()) {
            String match = matcher.group();
            BigDecimal number = new BigDecimal(match);
            String plainString = number.toPlainString();
            formula = formula.replace(match, plainString);
        }
        return formula;
    }

    private static boolean hasPrecedence(char operator1, char operator2) {
        return (operator2 == '*' || operator2 == '/') && (operator1 == '+' || operator1 == '-');
    }

    private static BigDecimal performOperation(BigDecimal a, BigDecimal b, char operator) {
        switch (operator) {
            case '+':
                return a.add(b);
            case '-':
                return a.subtract(b);
            case '*':
                return a.multiply(b);
            case '/':
                return a.divide(b, 8, BigDecimal.ROUND_HALF_UP); // 保留8位小数并四舍五入
            default:
                throw new IllegalArgumentException("Unsupported operator: " + operator);
        }
    }

可使用如下来测试:

    public static void main(String[] args) {
        System.out.println(calculate("-0.00000057e-12*tilt*tilt+0.00178274*tilt+0.95884259", BigDecimal.valueOf(45.34)));
        System.out.println(-0.00000057e-12 * 45.34 * 45.34 + 0.00178274 * 45.34 + 0.95884259);
    }

3. 结果显示,计算是正确的:

4. 代码解释:

这段代码是一个Java方法,名为 calculate,用于解析并计算一个数学表达式的结果。让我逐行解释它:

javaCopy code
public static BigDecimal calculate(String formula, BigDecimal tilt) {

这是一个公共静态方法的声明。它接受两个参数:一个是字符串 formula,表示数学表达式,另一个是 BigDecimal 类型的 tilt,用于替换表达式中的 “tilt”。

javaCopy code    // 去掉空格, 替换公式中的 "tilt"
    formula = formula.replaceAll(" ", "").replaceAll("tilt", tilt.toString());

这两行代码将表达式中的空格去掉,并将所有的 “tilt” 替换为 tilt 参数的字符串表示。

javaCopy code
    String[] tokens = formula.split("(?<=[-+*/])|(?=[-+*/])");

这行代码将表达式拆分成一个字符串数组 tokens,使用正则表达式 (?<=[-+*/])|(?=[-+*/]) 作为分隔符,这个正则表达式的意思是在运算符前后进行拆分。

javaCopy code    Stack<BigDecimal> numbers = new Stack<>();
    Stack<Character> operators = new Stack<>();

这两行代码创建了两个栈,一个用于存储数字,另一个用于存储运算符。

javaCopy code
    boolean isNegative = false; // 用于处理负数开头的情况

这行代码声明一个布尔变量 isNegative,用于跟踪是否遇到负数。

javaCopy code
    for (String token : tokens) {

这是一个循环,遍历拆分后的表达式中的每个 token

javaCopy code
        if (token.matches("[+\\-*/]")) {

这行代码检查 token 是否是一个运算符(+、-、*、/)。

javaCopy code            if (token.equals("-")) {
                if (isNegative) {
                    isNegative = false;
                    BigDecimal number = numbers.pop();
                    numbers.push(number.negate()); // 处理负数开头的情况
                } else {
                    isNegative = true;
                }
            }

这段代码处理负数的情况。如果 token 是负号 -,并且 isNegative 为真,那么从栈中弹出一个数,取其相反数,然后将结果推回栈中。否则,将 isNegative 设置为真,表示接下来的数是负数。

javaCopy code            } else {
                char operator = token.charAt(0);
                while (!operators.isEmpty() && hasPrecedence(operator, operators.peek())) {
                    BigDecimal b = numbers.pop();
                    BigDecimal a = numbers.pop();
                    char op = operators.pop();
                    BigDecimal result = performOperation(a, b, op);
                    numbers.push(result);
                }
                operators.push(operator);
            }

如果 token 不是运算符,那么它应该是一个数字。在这种情况下,首先检查运算符栈,如果栈不为空并且栈顶运算符的优先级高于当前 token 的运算符,就从栈中弹出运算符,并从数字栈中弹出两个数字进行计算,然后将结果推回数字栈中。然后,将当前 token 的运算符推到运算符栈中。

javaCopy code        } else {
            BigDecimal number = new BigDecimal(token);
            if (isNegative) {
                number = number.negate();
                isNegative = false;
            }
            numbers.push(number);
        }

如果 token 不是运算符,那么它应该是一个数字。在这种情况下,将 token 转换为 BigDecimal 数字,如果 isNegative 为真,就取其相反数,然后将结果推入数字栈中。

javaCopy code    }

    while (!operators.isEmpty()) {
        BigDecimal b = numbers.pop();
        BigDecimal a = numbers.pop();
        char op = operators.pop();
        BigDecimal result = performOperation(a, b, op);
        numbers.push(result);
    }

这个循环用于处理剩余的运算符和数字,直到运算符栈为空。对于每个运算符,从数字栈中弹出两个数字进行计算,然后将结果推回数字栈中。

javaCopy code
    return numbers.pop();

最后,返回数字栈中的最终结果,这个结果就是整个表达式的计算结果。

这段代码的目的是解析一个包含数学表达式的字符串,并计算其结果,包括处理负数和运算符的优先级。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值