提问人:Shah 提问时间:8/6/2010 最后编辑:PshemoShah 更新时间:4/14/2023 访问量:614323
如何计算以字符串形式给出的数学表达式?
How to evaluate a math expression given in string form?
问:
我正在尝试编写一个 Java 例程来计算数学表达式,例如:String
"5+3"
"10-4*5"
"(1+10)*3"
我想避免很多 if-then-else 语句。 我该怎么做?
答:
我认为无论你以何种方式做到这一点,它都会涉及很多条件语句。但是对于示例中的单个操作,您可以将其限制为 4 个 if 语句,如下所示
String math = "1+4";
if (math.split("+").length == 2) {
//do calculation
} else if (math.split("-").length == 2) {
//do calculation
} ...
当您想处理“4+5*6”等多个操作时,它会变得更加复杂。
如果你正在尝试构建一个计算器,那么我会将计算的每个部分(每个数字或运算符)分开传递,而不是作为一个字符串。
评论
解决此问题的正确方法是使用词法分析器和解析器。您可以自己编写这些页面的简单版本,或者这些页面还具有指向 Java 词法分析器和解析器的链接。
创建递归下降解析器是一个非常好的学习练习。
看来 JEP 应该做这项工作
在 JDK1.6 中,您可以使用内置的 Javascript 引擎。
import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
public class Test {
public static void main(String[] args) throws ScriptException {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
String foo = "40+2";
System.out.println(engine.eval(foo));
}
}
评论
return (Double) engine.eval(foo);
new javax.script.ScriptEngineManager().getEngineByName("JavaScript") .eval("var f = new java.io.FileWriter('hello.txt'); f.write('UNLIMITED POWER!'); f.close();");
import java.util.*;
public class check {
int ans;
String str="7 + 5";
StringTokenizer st=new StringTokenizer(str);
int v1=Integer.parseInt(st.nextToken());
String op=st.nextToken();
int v2=Integer.parseInt(st.nextToken());
if(op.equals("+")) { ans= v1 + v2; }
if(op.equals("-")) { ans= v1 - v2; }
//.........
}
本文讨论了各种方法。以下是文章中提到的 2 种关键方法:
来自 Apache 的 JEXL
允许包含对 Java 对象的引用的脚本。
// Create or retrieve a JexlEngine
JexlEngine jexl = new JexlEngine();
// Create an expression object
String jexlExp = "foo.innerFoo.bar()";
Expression e = jexl.createExpression( jexlExp );
// Create a context and add data
JexlContext jctx = new MapContext();
jctx.set("foo", new Foo() );
// Now evaluate the expression, getting the result
Object o = e.evaluate(jctx);
使用 JDK 中嵌入的 javascript 引擎:
private static void jsEvalWithVariable()
{
List<String> namesList = new ArrayList<String>();
namesList.add("Jill");
namesList.add("Bob");
namesList.add("Laureen");
namesList.add("Ed");
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");
jsEngine.put("namesListKey", namesList);
System.out.println("Executing in script environment...");
try
{
jsEngine.eval("var x;" +
"var names = namesListKey.toArray();" +
"for(x in names) {" +
" println(names[x]);" +
"}" +
"namesListKey.add(\"Dana\");");
}
catch (ScriptException ex)
{
ex.printStackTrace();
}
}
评论
您也可以尝试 BeanShell 解释器:
Interpreter interpreter = new Interpreter();
interpreter.eval("result = (7+21*6)/(32-27)");
System.out.println(interpreter.get("result"));
评论
像这样的东西怎么样:
String st = "10+3";
int result;
for(int i=0;i<st.length();i++)
{
if(st.charAt(i)=='+')
{
result=Integer.parseInt(st.substring(0, i))+Integer.parseInt(st.substring(i+1, st.length()));
System.out.print(result);
}
}
并相应地对其他所有数学运算符执行类似操作。
评论
HERE 是 GitHub 上另一个名为 EvalEx 的开源库。
与 JavaScript 引擎不同,该库仅专注于计算数学表达式。此外,该库是可扩展的,并支持使用布尔运算符和括号。
评论
另一种方法是使用Spring Expression Language或SpEL,它在评估数学表达式的同时做更多的事情,因此可能有点矫枉过正。您不必使用 Spring 框架来使用此表达式库,因为它是独立的。从 SpEL 的文档中复制示例:
ExpressionParser parser = new SpelExpressionParser();
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); //24.0
我为算术表达式编写了这个方法来回答这个问题。它执行加法、减法、乘法、除法、幂(使用符号)和一些基本函数,如 .它支持使用 ...(...)进行分组,并正确获取运算符优先级和关联性规则。eval
^
sqrt
(
)
public static double eval(final String str) {
return new Object() {
int pos = -1, ch;
void nextChar() {
ch = (++pos < str.length()) ? str.charAt(pos) : -1;
}
boolean eat(int charToEat) {
while (ch == ' ') nextChar();
if (ch == charToEat) {
nextChar();
return true;
}
return false;
}
double parse() {
nextChar();
double x = parseExpression();
if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
return x;
}
// Grammar:
// expression = term | expression `+` term | expression `-` term
// term = factor | term `*` factor | term `/` factor
// factor = `+` factor | `-` factor | `(` expression `)` | number
// | functionName `(` expression `)` | functionName factor
// | factor `^` factor
double parseExpression() {
double x = parseTerm();
for (;;) {
if (eat('+')) x += parseTerm(); // addition
else if (eat('-')) x -= parseTerm(); // subtraction
else return x;
}
}
double parseTerm() {
double x = parseFactor();
for (;;) {
if (eat('*')) x *= parseFactor(); // multiplication
else if (eat('/')) x /= parseFactor(); // division
else return x;
}
}
double parseFactor() {
if (eat('+')) return +parseFactor(); // unary plus
if (eat('-')) return -parseFactor(); // unary minus
double x;
int startPos = this.pos;
if (eat('(')) { // parentheses
x = parseExpression();
if (!eat(')')) throw new RuntimeException("Missing ')'");
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
x = Double.parseDouble(str.substring(startPos, this.pos));
} else if (ch >= 'a' && ch <= 'z') { // functions
while (ch >= 'a' && ch <= 'z') nextChar();
String func = str.substring(startPos, this.pos);
if (eat('(')) {
x = parseExpression();
if (!eat(')')) throw new RuntimeException("Missing ')' after argument to " + func);
} else {
x = parseFactor();
}
if (func.equals("sqrt")) x = Math.sqrt(x);
else if (func.equals("sin")) x = Math.sin(Math.toRadians(x));
else if (func.equals("cos")) x = Math.cos(Math.toRadians(x));
else if (func.equals("tan")) x = Math.tan(Math.toRadians(x));
else throw new RuntimeException("Unknown function: " + func);
} else {
throw new RuntimeException("Unexpected: " + (char)ch);
}
if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation
return x;
}
}.parse();
}
例:
System.out.println(eval("((4 - 2^3 + 1) * -sqrt(3*3+4*4)) / 2"));
输出:7.5(正确)
解析器是一个递归下降解析器,因此在内部对其语法中的每个运算符优先级使用单独的解析方法。我故意保持简短,但这里有一些你可能想扩展它的想法:
变量:
读取函数名称的解析器位也可以很容易地更改以处理自定义变量,方法是在传递给方法的变量表中查找名称,例如 .
eval
Map<String,Double> variables
单独编译和评估:
如果在添加了对变量的支持后,您希望使用更改的变量对相同的表达式进行数百万次计算,而不必每次都对其进行解析,该怎么办?有可能。首先定义一个用于计算预编译表达式的接口:
@FunctionalInterface interface Expression { double eval(); }
现在,要将原始的“eval”函数重新设计为“parse”函数,请更改所有返回 s 的方法,以便它们返回该接口的实例。Java 8 的 lambda 语法非常适合此目的。更改的方法之一的示例:
double
Expression parseExpression() { Expression x = parseTerm(); for (;;) { if (eat('+')) { // addition Expression a = x, b = parseTerm(); x = (() -> a.eval() + b.eval()); } else if (eat('-')) { // subtraction Expression a = x, b = parseTerm(); x = (() -> a.eval() - b.eval()); } else { return x; } } }
这将构建一个递归的对象树,该树表示已编译的表达式(抽象语法树)。然后,您可以编译一次,并使用不同的值重复计算它:
Expression
public static void main(String[] args) { Map<String,Double> variables = new HashMap<>(); Expression exp = parse("x^2 - x + 2", variables); for (double x = -20; x <= +20; x++) { variables.put("x", x); System.out.println(x + " => " + exp.eval()); } }
不同的数据类型:
您可以更改赋值器以使用更强大的功能,例如 ,或实现复数或有理数(分数)的类。您甚至可以使用 ,允许在表达式中混合使用一些数据类型,就像真正的编程语言一样。:)
double
BigDecimal
Object
此答案中的所有代码都已发布到公共领域。玩得愉快!
评论
这是另一个有趣的替代 https://github.com/Shy-Ta/expression-evaluator-demo
用法非常简单,可以完成工作,例如:
ExpressionsEvaluator evalExpr = ExpressionsFactory.create("2+3*4-6/2");
assertEquals(BigDecimal.valueOf(11), evalExpr.eval());
可以使用 Djikstra 的分流场算法将中缀表示法中的任何表达式字符串转换为后缀表示法。然后,该算法的结果可以用作后缀算法的输入,该算法返回表达式的结果。
评论
如果我们要实现它,那么我们可以使用以下算法:--
虽然仍有令牌需要读入,
1.1 获取下一个令牌。 1.2 如果令牌是:
1.2.1 一个数字:将其推送到值堆栈上。
1.2.2 变量:获取其值,并推送到值堆栈上。
1.2.3 左括号:将其推到算子堆栈上。
1.2.4 右括号:
1 While the thing on top of the operator stack is not a left parenthesis, 1 Pop the operator from the operator stack. 2 Pop the value stack twice, getting two operands. 3 Apply the operator to the operands, in the correct order. 4 Push the result onto the value stack. 2 Pop the left parenthesis from the operator stack, and discard it.
1.2.5 运算符(称之为 thisOp):
1 While the operator stack is not empty, and the top thing on the operator stack has the same or greater precedence as thisOp, 1 Pop the operator from the operator stack. 2 Pop the value stack twice, getting two operands. 3 Apply the operator to the operands, in the correct order. 4 Push the result onto the value stack. 2 Push thisOp onto the operator stack.
虽然算子堆栈不为空, 1 从运算符堆栈中弹出运算符。 2 弹出值堆栈两次,得到两个操作数。 3 按正确的顺序将运算符应用于操作数。 4 将结果推送到值堆栈上。
此时,运算符堆栈应为空,并且值 堆栈中应该只有一个值,即最终结果。
评论
现在回答为时已晚,但我遇到了同样的情况来评估 java 中的表达式,它可能会对某人有所帮助
MVEL
对表达式进行运行时评估,我们可以编写一个 Java 代码来对其进行评估。String
String expressionStr = "x+y";
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("x", 10);
vars.put("y", 20);
ExecutableStatement statement = (ExecutableStatement) MVEL.compileExpression(expressionStr);
Object result = MVEL.executeExpression(statement, vars);
评论
你可以看看 Symja 框架:
ExprEvaluator util = new ExprEvaluator();
IExpr result = util.evaluate("10-40");
System.out.println(result.toString()); // -> "-30"
请注意,可以计算更复杂的表达式:
// D(...) gives the derivative of the function Sin(x)*Cos(x)
IAST function = D(Times(Sin(x), Cos(x)), x);
IExpr result = util.evaluate(function);
// print: Cos(x)^2-Sin(x)^2
对于我的大学项目,我一直在寻找一个支持基本公式和更复杂方程(尤其是迭代运算符)的解析器/计算器。我发现了非常好的 JAVA 和 .NET 开源库,称为 mXparser。我将举几个例子来对语法有所了解,有关进一步的说明,请访问项目网站(尤其是教程部分)。
https://mathparser.org/mxparser-tutorial/
例子很少
1 - 简单的疣草
Expression e = new Expression("( 2 + 3/4 + sin(pi) )/2");
double v = e.calculate()
2 - 用户定义的参数和常量
Argument x = new Argument("x = 10");
Constant a = new Constant("a = pi^2");
Expression e = new Expression("cos(a*x)", x, a);
double v = e.calculate()
3 - 用户定义的函数
Function f = new Function("f(x, y, z) = sin(x) + cos(y*z)");
Expression e = new Expression("f(3,2,5)", f);
double v = e.calculate()
4 - 迭代
Expression e = new Expression("sum( i, 1, 100, sin(i) )");
double v = e.calculate()
最近发现 - 如果您想尝试语法(并查看高级用例),您可以下载由 mXparser 提供支持的 Scalar Calculator 应用程序。
评论
另一种选择:https://github.com/stefanhaustein/expressionparser
我已经实现了这一点,以便有一个简单但灵活的选项来允许两者:
- 即时处理(Calculator.java、SetDemo.java)
- 构建和处理解析树 (TreeBuilder.java)
上面链接的 TreeBuilder 是执行符号派生的 CAS 演示包的一部分。还有一个BASIC解释器示例,我已经开始使用它构建一个TypeScript解释器。
如果您的 Java 应用程序已经访问了数据库,则可以轻松计算表达式,而无需使用任何其他 JAR。
一些数据库要求你使用虚拟表(例如,Oracle 的“双重”表),而其他数据库则允许你计算表达式,而无需从任何表中“选择”。
例如,在 Sql Server 或 Sqlite 中
select (((12.10 +12.0))/ 233.0) amount
和在 Oracle 中
select (((12.10 +12.0))/ 233.0) amount from dual;
使用数据库的优点是可以同时计算多个表达式。此外,大多数数据库将允许您使用高度复杂的表达式,并且还具有许多可以根据需要调用的额外函数。
但是,如果需要单独计算许多单个表达式,特别是当数据库位于网络服务器上时,性能可能会受到影响。
下面通过使用 Sqlite 内存中数据库在一定程度上解决了性能问题。
下面是一个完整的 Java 工作示例
Class. forName("org.sqlite.JDBC");
Connection conn = DriverManager.getConnection("jdbc:sqlite::memory:");
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount");
rs.next();
System.out.println(rs.getBigDecimal(1));
stat.close();
conn.close();
当然,您可以扩展上面的代码以同时处理多个计算。
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount, (1+100)/20.0 amount2");
评论
package ExpressionCalculator.expressioncalculator;
import java.text.DecimalFormat;
import java.util.Scanner;
public class ExpressionCalculator {
private static String addSpaces(String exp){
//Add space padding to operands.
//https://regex101.com/r/sJ9gM7/73
exp = exp.replaceAll("(?<=[0-9()])[\\/]", " / ");
exp = exp.replaceAll("(?<=[0-9()])[\\^]", " ^ ");
exp = exp.replaceAll("(?<=[0-9()])[\\*]", " * ");
exp = exp.replaceAll("(?<=[0-9()])[+]", " + ");
exp = exp.replaceAll("(?<=[0-9()])[-]", " - ");
//Keep replacing double spaces with single spaces until your string is properly formatted
/*while(exp.indexOf(" ") != -1){
exp = exp.replace(" ", " ");
}*/
exp = exp.replaceAll(" {2,}", " ");
return exp;
}
public static Double evaluate(String expr){
DecimalFormat df = new DecimalFormat("#.####");
//Format the expression properly before performing operations
String expression = addSpaces(expr);
try {
//We will evaluate using rule BDMAS, i.e. brackets, division, power, multiplication, addition and
//subtraction will be processed in following order
int indexClose = expression.indexOf(")");
int indexOpen = -1;
if (indexClose != -1) {
String substring = expression.substring(0, indexClose);
indexOpen = substring.lastIndexOf("(");
substring = substring.substring(indexOpen + 1).trim();
if(indexOpen != -1 && indexClose != -1) {
Double result = evaluate(substring);
expression = expression.substring(0, indexOpen).trim() + " " + result + " " + expression.substring(indexClose + 1).trim();
return evaluate(expression.trim());
}
}
String operation = "";
if(expression.indexOf(" / ") != -1){
operation = "/";
}else if(expression.indexOf(" ^ ") != -1){
operation = "^";
} else if(expression.indexOf(" * ") != -1){
operation = "*";
} else if(expression.indexOf(" + ") != -1){
operation = "+";
} else if(expression.indexOf(" - ") != -1){ //Avoid negative numbers
operation = "-";
} else{
return Double.parseDouble(expression);
}
int index = expression.indexOf(operation);
if(index != -1){
indexOpen = expression.lastIndexOf(" ", index - 2);
indexOpen = (indexOpen == -1)?0:indexOpen;
indexClose = expression.indexOf(" ", index + 2);
indexClose = (indexClose == -1)?expression.length():indexClose;
if(indexOpen != -1 && indexClose != -1) {
Double lhs = Double.parseDouble(expression.substring(indexOpen, index));
Double rhs = Double.parseDouble(expression.substring(index + 2, indexClose));
Double result = null;
switch (operation){
case "/":
//Prevent divide by 0 exception.
if(rhs == 0){
return null;
}
result = lhs / rhs;
break;
case "^":
result = Math.pow(lhs, rhs);
break;
case "*":
result = lhs * rhs;
break;
case "-":
result = lhs - rhs;
break;
case "+":
result = lhs + rhs;
break;
default:
break;
}
if(indexClose == expression.length()){
expression = expression.substring(0, indexOpen) + " " + result + " " + expression.substring(indexClose);
}else{
expression = expression.substring(0, indexOpen) + " " + result + " " + expression.substring(indexClose + 1);
}
return Double.valueOf(df.format(evaluate(expression.trim())));
}
}
}catch(Exception exp){
exp.printStackTrace();
}
return 0.0;
}
public static void main(String args[]){
Scanner scanner = new Scanner(System.in);
System.out.print("Enter an Mathematical Expression to Evaluate: ");
String input = scanner.nextLine();
System.out.println(evaluate(input));
}
}
评论
使用 JDK1.6 的 Javascript 引擎和代码注入处理,尝试以下示例代码。
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class EvalUtil {
private static ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
public static void main(String[] args) {
try {
System.out.println((new EvalUtil()).eval("(((5+5)/2) > 5) || 5 >3 "));
System.out.println((new EvalUtil()).eval("(((5+5)/2) > 5) || true"));
} catch (Exception e) {
e.printStackTrace();
}
}
public Object eval(String input) throws Exception{
try {
if(input.matches(".*[a-zA-Z;~`#$_{}\\[\\]:\\\\;\"',\\.\\?]+.*")) {
throw new Exception("Invalid expression : " + input );
}
return engine.eval(input);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
这实际上是对@Boann给出的答案的补充。它有一个小错误,导致“-2 ^ 2”给出 -4.0 的错误结果。问题在于在他的中计算幂的点。只需将幂移动到 parseTerm() 块,您就会没事。看看下面,这是@Boann的回答略有修改。修改在评论中。
public static double eval(final String str) {
return new Object() {
int pos = -1, ch;
void nextChar() {
ch = (++pos < str.length()) ? str.charAt(pos) : -1;
}
boolean eat(int charToEat) {
while (ch == ' ') nextChar();
if (ch == charToEat) {
nextChar();
return true;
}
return false;
}
double parse() {
nextChar();
double x = parseExpression();
if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
return x;
}
// Grammar:
// expression = term | expression `+` term | expression `-` term
// term = factor | term `*` factor | term `/` factor
// factor = `+` factor | `-` factor | `(` expression `)`
// | number | functionName factor | factor `^` factor
double parseExpression() {
double x = parseTerm();
for (;;) {
if (eat('+')) x += parseTerm(); // addition
else if (eat('-')) x -= parseTerm(); // subtraction
else return x;
}
}
double parseTerm() {
double x = parseFactor();
for (;;) {
if (eat('*')) x *= parseFactor(); // multiplication
else if (eat('/')) x /= parseFactor(); // division
else if (eat('^')) x = Math.pow(x, parseFactor()); //exponentiation -> Moved in to here. So the problem is fixed
else return x;
}
}
double parseFactor() {
if (eat('+')) return parseFactor(); // unary plus
if (eat('-')) return -parseFactor(); // unary minus
double x;
int startPos = this.pos;
if (eat('(')) { // parentheses
x = parseExpression();
eat(')');
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
x = Double.parseDouble(str.substring(startPos, this.pos));
} else if (ch >= 'a' && ch <= 'z') { // functions
while (ch >= 'a' && ch <= 'z') nextChar();
String func = str.substring(startPos, this.pos);
x = parseFactor();
if (func.equals("sqrt")) x = Math.sqrt(x);
else if (func.equals("sin")) x = Math.sin(Math.toRadians(x));
else if (func.equals("cos")) x = Math.cos(Math.toRadians(x));
else if (func.equals("tan")) x = Math.tan(Math.toRadians(x));
else throw new RuntimeException("Unknown function: " + func);
} else {
throw new RuntimeException("Unexpected: " + (char)ch);
}
//if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation -> This is causing a bit of problem
return x;
}
}.parse();
}
评论
-2^2 = -4
其实是正常的,不是bug。它被分组为 .例如,在 Desmos 上尝试一下。您的代码实际上引入了几个错误。首先是不再从右到左分组。换句话说,应该是 group like,因为是右关联,但您的修改使它像 group like 。第二个是它应该具有比 和 更高的优先级,但您的修改会以相同的方式对待它。请参见 ideone.com/iN2mMa。-(2^2)
^
2^3^2
2^(3^2)
^
(2^3)^2
^
*
/
一个可以计算数学表达式的 Java 类:
package test;
public class Calculator {
public static Double calculate(String expression){
if (expression == null || expression.length() == 0) {
return null;
}
return calc(expression.replace(" ", ""));
}
public static Double calc(String expression) {
String[] containerArr = new String[]{expression};
double leftVal = getNextOperand(containerArr);
expression = containerArr[0];
if (expression.length() == 0) {
return leftVal;
}
char operator = expression.charAt(0);
expression = expression.substring(1);
while (operator == '*' || operator == '/') {
containerArr[0] = expression;
double rightVal = getNextOperand(containerArr);
expression = containerArr[0];
if (operator == '*') {
leftVal = leftVal * rightVal;
} else {
leftVal = leftVal / rightVal;
}
if (expression.length() > 0) {
operator = expression.charAt(0);
expression = expression.substring(1);
} else {
return leftVal;
}
}
if (operator == '+') {
return leftVal + calc(expression);
} else {
return leftVal - calc(expression);
}
}
private static double getNextOperand(String[] exp){
double res;
if (exp[0].startsWith("(")) {
int open = 1;
int i = 1;
while (open != 0) {
if (exp[0].charAt(i) == '(') {
open++;
} else if (exp[0].charAt(i) == ')') {
open--;
}
i++;
}
res = calc(exp[0].substring(1, i - 1));
exp[0] = exp[0].substring(i);
} else {
int i = 1;
if (exp[0].charAt(0) == '-') {
i++;
}
while (exp[0].length() > i && isNumber((int) exp[0].charAt(i))) {
i++;
}
res = Double.parseDouble(exp[0].substring(0, i));
exp[0] = exp[0].substring(i);
}
return res;
}
private static boolean isNumber(int c) {
int zero = (int) '0';
int nine = (int) '9';
return (c >= zero && c <= nine) || c =='.';
}
public static void main(String[] args) {
System.out.println(calculate("(((( -6 )))) * 9 * -1"));
System.out.println(calc("(-5.2+-5*-5*((5/4+2)))"));
}
}
评论
像 RHINO 或 NASHORN 这样的外部库可用于运行 javascript。javascript 可以在不解析字符串的情况下计算简单的公式。如果代码写得好,也不会对性能产生影响。 下面是 RHINO 的示例 -
public class RhinoApp {
private String simpleAdd = "(12+13+2-2)*2+(12+13+2-2)*2";
public void runJavaScript() {
Context jsCx = Context.enter();
Context.getCurrentContext().setOptimizationLevel(-1);
ScriptableObject scope = jsCx.initStandardObjects();
Object result = jsCx.evaluateString(scope, simpleAdd , "formula", 0, null);
Context.exit();
System.out.println(result);
}
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class test2 {
public static void main(String[] args) throws ScriptException {
String s = "10+2";
ScriptEngineManager mn = new ScriptEngineManager();
ScriptEngine en = mn.getEngineByName("js");
Object result = en.eval(s);
System.out.println(result);
}
}
评论
我已经完成了迭代解析和分流 Yard 算法的使用,我真的很喜欢开发表达式计算器,您可以在这里找到所有代码
https://github.com/nagaraj200788/JavaExpressionEvaluator
有 73 个测试用例,甚至适用于 Bigintegers、Bigdecimals
支持所有关系表达式、算术表达式以及两者的组合。 甚至支持三元算子。
添加了增强功能以支持有符号数字,例如 -100+89 它很有意义,有关详细信息,请查看 TokenReader.isUnaryOperator() 方法,我更新了上面链接中的代码
如果有人需要精确的十进制数,我做了一个修改,它使用 而不是 .BigDecimal
double
我从 Boann 的回答中获得了灵感,并将其修改为 .BigDecimal
public static BigDecimal eval(final String str, MathContext mathContext) {
if (ObjectUtils.isEmpty(str)) {
return null;
}
return new Object() {
int pos = -1, ch;
void nextChar() {
ch = (++pos < str.length()) ? str.charAt(pos) : -1;
}
boolean eat(int charToEat) {
while (ch == ' ') nextChar();
if (ch == charToEat) {
nextChar();
return true;
}
return false;
}
BigDecimal parse() {
nextChar();
BigDecimal x = parseExpression();
if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
return x;
}
// Grammar:
// expression = term | expression `+` term | expression `-` term
// term = factor | term `*` factor | term `/` factor
// factor = `+` factor | `-` factor | `(` expression `)` | number
// | functionName `(` expression `)` | functionName factor
// | factor `^` factor
BigDecimal parseExpression() {
BigDecimal x = parseTerm();
for (;;) {
if (eat('+')) x = x.add(parseTerm()); // addition
else if (eat('-')) x = x.subtract(parseTerm()); // subtraction
else return x;
}
}
BigDecimal parseTerm() {
BigDecimal x = parseFactor();
for (;;) {
if (eat('*')) x = x.multiply(parseFactor()); // multiplication
else if (eat('/')) x = x.divide(parseFactor(), mathContext); // division
else return x;
}
}
BigDecimal parseFactor() {
if (eat('+')) return parseFactor(); // unary plus
if (eat('-')) return parseFactor().negate(); // unary minus
BigDecimal x;
int startPos = this.pos;
if (eat('(')) { // parentheses
x = parseExpression();
if (!eat(')')) throw new RuntimeException("Missing ')'");
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
x = new BigDecimal(str.substring(startPos, this.pos));
} else if (ch >= 'a' && ch <= 'z') { // functions
while (ch >= 'a' && ch <= 'z') nextChar();
String func = str.substring(startPos, this.pos);
if (eat('(')) {
x = parseExpression();
if (!eat(')')) throw new RuntimeException("Missing ')' after argument to " + func);
} else {
x = parseFactor();
}
if ("sqrt".equals(func)) {
x = x.sqrt(mathContext);
} else {
throw new RuntimeException("Unknown function: " + func);
}
} else {
throw new RuntimeException("Unexpected: " + (char)ch);
}
if (eat('^')) x = x.pow(parseFactor().intValue(), mathContext); // exponentiation
return x;
}
}.parse();
}
按以下方式使用它:
// set precision and rounding mode
MathContext mathContext = new MathContext(10, RoundingMode.HALF_UP);
BigDecimal result = YourLib.eval("33+33", mathContext);
评论