Sha*_*hah 303 java string math
我正在尝试编写一个Java例程来评估简单的数学表达式,String例如:
"5+3""10-40""10*3"我想避免很多if-then-else语句.我怎样才能做到这一点?
Rea*_*wTo 361
使用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));
}
}
Run Code Online (Sandbox Code Playgroud)
Boa*_*ann 211
我已经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 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();
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
return x;
}
}.parse();
}
Run Code Online (Sandbox Code Playgroud)
例:
System.out.println(eval("((4 - 2^3 + 1) * -sqrt(3*3+4*4)) / 2"));
Run Code Online (Sandbox Code Playgroud)
输出:7.5 (这是正确的)
解析器是递归下降解析器,因此内部对其语法中的每个级别的运算符优先级使用单独的解析方法.我保持简短,所以很容易修改,但这里有一些想法,你可能想要扩展它:
变量:
通过查找传递给eval方法的变量表中的名称(如a),可以轻松更改读取函数名称的解析器位以处理自定义变量Map<String,Double> variables.
单独的编译和评估:
如果在添加对变量的支持后,您希望使用已更改的变量对相同的表达式进行数百万次计算,而不是每次都进行解析,该怎么办?这是可能的.首先定义用于评估预编译表达式的接口:
@FunctionalInterface
interface Expression {
double eval();
}
Run Code Online (Sandbox Code Playgroud)
现在更改返回doubles的所有方法,因此它们返回该接口的实例.Java 8的lambda语法非常适用于此.其中一个更改方法的示例:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这构建了一个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());
}
}
Run Code Online (Sandbox Code Playgroud)不同的数据类型:
而不是double,你可以改变评估者使用更强大的东西BigDecimal,或者实现复杂数字或有理数(分数)的类.您甚至可以使用Object,允许在表达式中混合使用某种数据类型,就像真正的编程语言一样.:)
此答案中的所有代码都发布到公共领域.玩得开心!
Ler*_*gan 23
对于我的大学项目,我一直在寻找支持基本公式和更复杂方程(特别是迭代运算符)的解析器/求值器.我找到了非常好的JAVA和.NET开源库,名为mXparser.我将举几个例子来对语法有所了解,如需进一步说明,请访问项目网站(特别是教程部分).
http://mathparser.org/mxparser-tutorial/
几个例子
1 - 简单的furmula
Expression e = new Expression("( 2 + 3/4 + sin(pi) )/2");
double v = e.calculate()
Run Code Online (Sandbox Code Playgroud)
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()
Run Code Online (Sandbox Code Playgroud)
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()
Run Code Online (Sandbox Code Playgroud)
4 - 迭代
Expression e = new Expression("sum( i, 1, 100, sin(i) )");
double v = e.calculate()
Run Code Online (Sandbox Code Playgroud)
最好的祝福
mar*_*ner 14
您还可以尝试使用BeanShell解释器:
Interpreter interpreter = new Interpreter();
interpreter.eval("result = (7+21*6)/(32-27)");
System.out.println(interpreter.get("result"));
Run Code Online (Sandbox Code Playgroud)
DAB*_*DAB 14
如果Java应用程序已经访问数据库,则可以轻松地评估表达式,而无需使用任何其他JAR.
有些数据库要求您使用虚拟表(例如,Oracle的"双"表),而其他数据库则允许您在不从任何表"选择"的情况下评估表达式.
例如,在Sql Server或Sqlite中
select (((12.10 +12.0))/ 233.0) amount
Run Code Online (Sandbox Code Playgroud)
在Oracle中
select (((12.10 +12.0))/ 233.0) amount from dual;
Run Code Online (Sandbox Code Playgroud)
使用DB的优点是您可以同时评估多个表达式.此外,大多数DB都允许您使用高度复杂的表达式,并且还可以根据需要调用许多额外的函数.
但是,如果需要单独评估许多单个表达式,特别是当DB位于网络服务器上时,性能可能会受到影响.
以下通过使用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();
Run Code Online (Sandbox Code Playgroud)
当然,您可以扩展上面的代码以同时处理多个计算.
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount, (1+100)/20.0 amount2");
Run Code Online (Sandbox Code Playgroud)
本文指出了3种不同的方法,一种是来自Apache的JEXL,并允许包含对java对象的引用的脚本.
另一种方法是使用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
Run Code Online (Sandbox Code Playgroud)
这是另一个有趣的替代方法 https://github.com/Shy-Ta/expression-evaluator-demo
用法非常简单,可以完成工作,例如:
ExpressionsEvaluator evalExpr = ExpressionsFactory.create("2+3*4-6/2");
assertEquals(BigDecimal.valueOf(11), evalExpr.eval());
Run Code Online (Sandbox Code Playgroud)
小智 6
如果我们要实现它,那么我们可以使用以下算法: -
虽然还有令牌需要阅读,
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.
Run Code Online (Sandbox Code Playgroud)
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.
Run Code Online (Sandbox Code Playgroud)当操作员堆栈不为空时,1从操作员堆栈中弹出操作员.2弹出值栈两次,得到两个操作数.3以正确的顺序将操作符应用于操作数.4将结果推送到值堆栈.
此时操作符堆栈应该为空,并且值堆栈中应该只有一个值,这是最终结果.
小智 5
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; }
//.........
}
Run Code Online (Sandbox Code Playgroud)
现在回答为时已晚,但我遇到了同样的情况来评估 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);
Run Code Online (Sandbox Code Playgroud)
使用 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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这实际上是对@Boann 给出的答案的补充。它有一个小错误,导致“-2 ^ 2”给出错误结果 -4.0。问题在于在 his 中求幂的点。只需将求幂移至 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();
}
Run Code Online (Sandbox Code Playgroud)