数学表达式解析

本文最后更新于:2026年3月30日 凌晨

数学表达式解析

最近碰到一个需求,要把字符串形式的表达式解析出来,交给前端或者后端去计算。比如这种:

1
2 * sum(2, 3) - x

核心要求不算复杂,主要是这几条:

  • 支持基本的加减乘除和括号。
  • 支持变量。
  • 支持自定义函数。
  • 最好前后端都能找到比较顺手的实现方式。

我分别看了 Java、Python 和 JavaScript 的方案,这里做个记录。

Java

1. exp4j

官网:

https://www.objecthunter.net/exp4j/#Custom_functions

参考链接:

https://ibit.tech/archives/exp4j-introduction

exp4j 对基础数学表达式的支持挺完整,接入也比较轻,语法直观,算是上手成本最低的一类方案。

它的问题主要出在自定义函数上。Function 必须提前声明参数个数,所以像 sum(1, 2, 3, 4) 这种可变参数函数,实现起来就不太舒服。

如果你的函数基本都是固定参数,比如 max(a, b)round(x) 这种,那 exp4j 其实已经够用了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 几个方案里,exp4j 算是最容易上手的
public class Main {
static Function sumFn = new Function("sum", 3) {
@Override
public double apply(double... args) {
double sumResult = 0.0;
for (double number : args) {
sumResult += number;
}
return sumResult;
}
};

public static void main(String[] args) {
Expression expression = new ExpressionBuilder("2 * sum(2, sum(4, 5, 1), 2) - x")
.function(sumFn)
.variables("x")
.build()
.setVariable("x", 2);

double result = expression.evaluate();
System.out.println("Result: " + result); // 26
}
}

2. JEP

官网:

https://www.singularsys.com/jep/doc/html/usage.html#gettingstarted

JEP 的能力会更完整一些,但我看了下文档和示例,整体用法对这次需求来说有点重,语法也不是我特别喜欢的风格,所以没有继续往下试。

3. ibit-exp4j

地址:

https://github.com/ibit-tech/ibit-exp4j

它是在 exp4j 基础上又封装了一层,但我实际看下来,并没有比直接使用 exp4j 更顺手,反而多了一层理解成本。

Java 小结

如果是 Java 场景,而且表达式能力不算特别复杂,我最后还是会优先选 exp4j。它不完美,但足够轻,也足够直接;真正的短板主要在“可变参数自定义函数”这里。

Python

Python 这边最直接的办法其实就是 eval

它最大的优点是省事:几乎不用额外引库,表达式、变量、自定义函数都能直接跑起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def add(x, y):
return x + y


def sub(x, y):
return x - y


global_vars = {"add": add, "sub": sub}
local_vars = {"x": 3, "y": 4}

expression = "sub(5, 4) + add(x, y) * 2"
result = eval(expression, global_vars, local_vars)
print(result) # 15

global_varslocal_vars 分别用来控制可用的函数和变量。如果不传,默认会落到当前全局作用域。

eval 最大的问题也很明显:它太灵活了,所以安全性会差一些。

如果表达式内容来自用户输入,或者来自不可信配置,直接使用 eval 风险非常大。这种方式更适合:

  • 内部系统。
  • 表达式来源完全可控的场景。
  • 临时脚本或者原型验证。

如果要做成正式产品能力,最好还是加一层白名单和语法限制,或者直接换成更安全的表达式解析方案。

JavaScript

JavaScript 这边我最后看到的是 math.js,整体体验是三种语言里最顺手的。

官网:

https://mathjs.org/docs/expressions/parsing.html

它不只是支持普通数学表达式,还支持变量、函数、单位换算,parser 的状态管理也比较方便。

1
2
3
4
5
6
7
8
9
10
11
12
const parser = math.parser()

parser.evaluate('sqrt(3^2 + 4^2)')
parser.evaluate('x = 7 / 2')
parser.evaluate('f(x, y) = x^y')
parser.evaluate('f(2, 3)')

parser.set('hello', function (name) {
return 'hello, ' + name + '!'
})

parser.evaluate('hello("user")')

如果前端要做表达式输入、预计算、公式编辑器这类功能,math.js 的可用性确实很高,能省掉不少自己造轮子的工作。

最后怎么选

如果只基于这次需求,我的结论大概是这样:

  • Java:选 exp4j,适合基础数学表达式和固定参数自定义函数。
  • Python:eval 最省事,但一定要限定在可信输入场景。
  • JavaScript:math.js 最完整,前端场景基本可以直接用。

如果是要做一套“允许用户自由输入公式”的通用能力,我会更偏向下面这种组合:

  • 前端用 math.js
  • 后端不要直接用 eval
  • Java 侧如果表达式规则相对固定,就用 exp4j

补一句

这类需求真正难的,往往不是“算出来”,而是“让用户能安全、稳定、可控地算出来”。

语法支持只是第一步,后面通常还会碰到这些问题:

  • 函数白名单怎么限制。
  • 变量怎么注入。
  • 非法表达式怎么报错。
  • 精度怎么处理。

所以表达式解析这玩意,看起来像是个小功能,做起来其实也挺不容易哦。


数学表达式解析
http://bestkele.com/2023/07/26/investigation/expression/
作者
kele
发布于
2023年7月26日
许可协议