杰瑞科技汇

Java字符串表达式如何直接计算?

Java 字符串表达式计算

在 Java 中计算字符串表达式有几种常见方法,以下是几种实现方式:

Java字符串表达式如何直接计算?-图1
(图片来源网络,侵删)

使用 ScriptEngine (Nashorn)

Java 8+ 内置了 Nashorn JavaScript 引擎,可以用来计算 JavaScript 表达式:

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class StringExpressionEvaluator {
    public static void main(String[] args) {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
        try {
            // 计算简单表达式
            Object result = engine.eval("2 + 3 * 4");
            System.out.println(result); // 输出: 14
            // 计算复杂表达式
            result = engine.eval("(5 + 3) * (10 - 4) / 2");
            System.out.println(result); // 输出: 24
            // 使用变量
            engine.eval("var x = 10; var y = 20;");
            result = engine.eval("x + y * 2");
            System.out.println(result); // 输出: 50
        } catch (ScriptException e) {
            e.printStackTrace();
        }
    }
}

使用第三方库 (exp4j)

exp4j 是一个专门用于数学表达式计算的轻量级库:

import net.objecthunter.exp4j.Expression;
import net.objecthunter.exp4j.ExpressionBuilder;
public class Exp4jExample {
    public static void main(String[] args) {
        // 创建表达式
        Expression e = new ExpressionBuilder("x + y * z")
            .variables("x", "y", "z")
            .build()
            .setVariable("x", 2)
            .setVariable("y", 3)
            .setVariable("z", 4);
        try {
            double result = e.evaluate();
            System.out.println(result); // 输出: 14.0
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

使用第三方库 (MVEL)

MVEL 是一个功能强大的表达式语言和引擎:

import org.mvel2.MVEL;
import java.util.HashMap;
import java.util.Map;
public class MvelExample {
    public static void main(String[] args) {
        String expression = "x + y * z";
        Map<String, Object> variables = new HashMap<>();
        variables.put("x", 2);
        variables.put("y", 3);
        variables.put("z", 4);
        Object result = MVEL.eval(expression, variables);
        System.out.println(result); // 输出: 14
    }
}

自定义解析器 (简单实现)

对于简单表达式,可以自己实现一个基本的解析器:

Java字符串表达式如何直接计算?-图2
(图片来源网络,侵删)
import java.util.Stack;
public class SimpleExpressionEvaluator {
    public static double evaluate(String expression) {
        Stack<Double> numbers = new Stack<>();
        Stack<Character> ops = new Stack<>();
        for (int i = 0; i < expression.length(); i++) {
            char c = expression.charAt(i);
            if (c == ' ') continue;
            if (Character.isDigit(c) || c == '.') {
                StringBuilder sb = new StringBuilder();
                while (i < expression.length() && 
                      (Character.isDigit(expression.charAt(i)) || expression.charAt(i) == '.')) {
                    sb.append(expression.charAt(i++));
                }
                i--;
                numbers.push(Double.parseDouble(sb.toString()));
            } else if (c == '(') {
                ops.push(c);
            } else if (c == ')') {
                while (ops.peek() != '(') {
                    numbers.push(applyOp(ops.pop(), numbers.pop(), numbers.pop()));
                }
                ops.pop();
            } else if (isOperator(c)) {
                while (!ops.isEmpty() && hasPrecedence(c, ops.peek())) {
                    numbers.push(applyOp(ops.pop(), numbers.pop(), numbers.pop()));
                }
                ops.push(c);
            }
        }
        while (!ops.isEmpty()) {
            numbers.push(applyOp(ops.pop(), numbers.pop(), numbers.pop()));
        }
        return numbers.pop();
    }
    private static boolean isOperator(char c) {
        return c == '+' || c == '-' || c == '*' || c == '/';
    }
    private static boolean hasPrecedence(char op1, char op2) {
        if (op2 == '(' || op2 == ')') return false;
        return (op1 != '*' && op1 != '/') || (op2 != '+' && op2 != '-');
    }
    private static double applyOp(char op, double b, double a) {
        switch (op) {
            case '+': return a + b;
            case '-': return a - b;
            case '*': return a * b;
            case '/': 
                if (b == 0) throw new UnsupportedOperationException("Cannot divide by zero");
                return a / b;
        }
        return 0;
    }
    public static void main(String[] args) {
        String expression = "3 + 5 * 2 - 8 / 4";
        System.out.println(evaluate(expression)); // 输出: 12.0
    }
}

使用 Java 8 的 ScriptEngine (更安全的版本)

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class SafeExpressionEvaluator {
    public static double evaluate(String expression) throws ScriptException {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
        // 使用 Function 包装表达式,避免直接执行任意代码
        String wrapped = "function evaluate() { return " + expression + "; }";
        engine.eval(wrapped);
        Object result = engine.eval("evaluate();");
        return ((Number) result).doubleValue();
    }
    public static void main(String[] args) {
        try {
            System.out.println(evaluate("2 + 3 * 4")); // 14.0
            System.out.println(evaluate("(5 + 3) * (10 - 4) / 2")); // 24.0
        } catch (ScriptException e) {
            System.err.println("Invalid expression: " + e.getMessage());
        }
    }
}

安全注意事项

当处理来自不可信源的字符串表达式时,需要注意安全风险:

  1. 避免直接执行任意代码(如 Nashorn 可以执行任意 JavaScript 代码)
  2. 考虑使用沙箱环境或限制可用的操作
  3. 对输入进行验证和清理
  4. 对于数学表达式,优先选择专门的数学表达式库(如 exp4j)

方法各有优缺点,选择哪种方法取决于你的具体需求、性能要求和安全性考虑。

分享:
扫描分享到社交APP
上一篇
下一篇