Estoy tratando de escribir una rutina Java para evaluar expresiones matemáticas simples a partir de String
valores como:
"5+3"
"10-40"
"10*3"
Quiero evitar muchas declaraciones if-then-else. ¿Cómo puedo hacer esto?
Estoy tratando de escribir una rutina Java para evaluar expresiones matemáticas simples a partir de String
valores como:
"5+3"
"10-40"
"10*3"
Quiero evitar muchas declaraciones if-then-else. ¿Cómo puedo hacer esto?
Respuestas:
Con JDK1.6, puede usar el motor Javascript incorporado.
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();");
- escribirá un archivo vía JavaScript en (por defecto) el directorio actual del programa
He escrito este eval
método para expresiones aritméticas para responder a esta pregunta. Realiza suma, resta, multiplicación, división, exponenciación (usando el ^
símbolo) y algunas funciones básicas como sqrt
. Admite la agrupación usando (
... )
, y obtiene la precedencia del operador y las reglas de asociatividad correctas.
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();
}
Ejemplo:
System.out.println(eval("((4 - 2^3 + 1) * -sqrt(3*3+4*4)) / 2"));
Salida: 7.5 (que es correcto)
El analizador es un analizador de descenso recursivo , por lo que utiliza internamente métodos de análisis separados para cada nivel de precedencia del operador en su gramática. Lo mantuve breve para que sea fácil de modificar, pero aquí hay algunas ideas con las que puede expandirlo:
Variables:
El bit del analizador que lee los nombres de las funciones también se puede cambiar fácilmente para manejar variables personalizadas, buscando nombres en una tabla de variables que se pasa al eval
método, como a Map<String,Double> variables
.
Compilación y evaluación separadas:
¿Qué pasa si, después de haber agregado soporte para variables, desea evaluar la misma expresión millones de veces con variables cambiadas, sin analizarla cada vez? Es posible. Primero defina una interfaz para usar para evaluar la expresión precompilada:
@FunctionalInterface
interface Expression {
double eval();
}
Ahora cambie todos los métodos que devuelven double
s, por lo que en cambio devuelven una instancia de esa interfaz. La sintaxis lambda de Java 8 funciona muy bien para esto. Ejemplo de uno de los métodos modificados:
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;
}
}
}
Eso construye un árbol recursivo de Expression
objetos que representan la expresión compilada (un árbol de sintaxis abstracta ). Luego puede compilarlo una vez y evaluarlo repetidamente con diferentes valores:
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());
}
}
Diferentes tipos de datos:
En lugar de double
, podría cambiar el evaluador para usar algo más poderoso BigDecimal
, como una clase que implemente números complejos o números racionales (fracciones). Incluso podría usar Object
, permitiendo una combinación de tipos de datos en expresiones, al igual que un lenguaje de programación real. :)
Todo el código en esta respuesta se libera al dominio público . ¡Que te diviertas!
double x = parseTerm();
evalúa el operador izquierdo, después de esto for (;;) {...}
evalúa las operaciones sucesivas del nivel de orden real (suma, resta). La misma lógica es y en el método parseTerm. ParseFactor no tiene el siguiente nivel, por lo que solo hay evaluaciones de métodos / variables o, en caso de parálisis, evalúe la subexpresión. El boolean eat(int charToEat)
método verifica la igualdad del carácter del cursor actual con el carácter charToEat, si igual devuelve verdadero y mueve el cursor al siguiente carácter, utilizo el nombre 'aceptar' para ello.
La forma correcta de resolver esto es con un lexer y un analizador . Puede escribir versiones simples de estos usted mismo, o esas páginas también tienen enlaces a lexers y analizadores de Java.
Crear un analizador de descenso recursivo es un muy buen ejercicio de aprendizaje.
Para mi proyecto universitario, estaba buscando un analizador / evaluador que soporte tanto fórmulas básicas como ecuaciones más complicadas (especialmente operadores iterados). Encontré una biblioteca de código abierto muy agradable para JAVA y .NET llamada mXparser. Daré algunos ejemplos para que sientan algo sobre la sintaxis. Para obtener más instrucciones, visite el sitio web del proyecto (especialmente la sección de tutoriales).
https://mathparser.org/mxparser-tutorial/
Y pocos ejemplos
1 - Fórmula simple
Expression e = new Expression("( 2 + 3/4 + sin(pi) )/2");
double v = e.calculate()
2 - Argumentos y constantes definidos por el usuario
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 - Funciones definidas por el usuario
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 - Iteración
Expression e = new Expression("sum( i, 1, 100, sin(i) )");
double v = e.calculate()
Encontrado recientemente: en caso de que desee probar la sintaxis (y ver el caso de uso avanzado), puede descargar la aplicación Scalar Calculator que funciona con mXparser.
Atentamente
AQUÍ hay otra biblioteca de código abierto en GitHub llamada EvalEx.
A diferencia del motor de JavaScript, esta biblioteca se centra únicamente en la evaluación de expresiones matemáticas. Además, la biblioteca es extensible y admite el uso de operadores booleanos, así como paréntesis.
También puede probar el intérprete BeanShell :
Interpreter interpreter = new Interpreter();
interpreter.eval("result = (7+21*6)/(32-27)");
System.out.println(interpreter.get("result"));
Puede evaluar expresiones fácilmente si su aplicación Java ya accede a una base de datos, sin usar ningún otro JAR.
Algunas bases de datos requieren que use una tabla ficticia (por ejemplo, la tabla "dual" de Oracle) y otras le permitirán evaluar expresiones sin "seleccionar" de ninguna tabla.
Por ejemplo, en SQL Server o SQLite
select (((12.10 +12.0))/ 233.0) amount
y en Oracle
select (((12.10 +12.0))/ 233.0) amount from dual;
La ventaja de usar una base de datos es que puede evaluar muchas expresiones al mismo tiempo. Además, la mayoría de las bases de datos le permitirán usar expresiones muy complejas y también tendrán una serie de funciones adicionales que se pueden invocar según sea necesario.
Sin embargo, el rendimiento puede verse afectado si es necesario evaluar individualmente muchas expresiones individuales, especialmente cuando la base de datos se encuentra en un servidor de red.
A continuación se aborda el problema de rendimiento hasta cierto punto, mediante el uso de una base de datos en memoria Sqlite.
Aquí hay un ejemplo de trabajo completo en 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();
Por supuesto, puede extender el código anterior para manejar múltiples cálculos al mismo tiempo.
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount, (1+100)/20.0 amount2");
Este artículo analiza varios enfoques. Aquí están los 2 enfoques clave mencionados en el artículo:
Permite scripts que incluyen referencias a objetos 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);
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();
}
}
Otra forma es usar Spring Expression Language o SpEL, que hace mucho más junto con la evaluación de expresiones matemáticas, por lo tanto, puede ser un poco exagerado. No tiene que usar Spring Framework para usar esta biblioteca de expresiones, ya que es independiente. Copiar ejemplos de la documentación de 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
Lea más ejemplos concisos de SpEL aquí y los documentos completos aquí.
si vamos a implementarlo, podemos usar el siguiente algoritmo:
Si bien todavía hay tokens para leer,
1.1 Obtenga el siguiente token. 1.2 Si el token es:
1.2.1 Un número: empujarlo a la pila de valores.
1.2.2 Una variable: obtenga su valor y empuje a la pila de valores.
1.2.3 Un paréntesis izquierdo: empújelo sobre la pila del operador.
1.2.4 Un paréntesis correcto:
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 Un operador (llámelo 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.
Mientras la pila de operadores no está vacía, 1 saque el operador de la pila de operadores. 2 Pop la pila de valores dos veces, obteniendo dos operandos. 3 Aplique el operador a los operandos, en el orden correcto. 4 Empuje el resultado en la pila de valores.
En este punto, la pila de operadores debe estar vacía, y la pila de valores debe tener solo un valor, que es el resultado final.
Esta es otra alternativa interesante https://github.com/Shy-Ta/expression-evaluator-demo
El uso es muy simple y hace el trabajo, por ejemplo:
ExpressionsEvaluator evalExpr = ExpressionsFactory.create("2+3*4-6/2");
assertEquals(BigDecimal.valueOf(11), evalExpr.eval());
Creo que de cualquier manera que hagas esto implicará muchas declaraciones condicionales. Pero para operaciones individuales como en sus ejemplos, podría limitarlo a 4 si las declaraciones con algo como
String math = "1+4";
if (math.split("+").length == 2) {
//do calculation
} else if (math.split("-").length == 2) {
//do calculation
} ...
Se vuelve mucho más complicado cuando quieres lidiar con múltiples operaciones como "4 + 5 * 6".
Si está tratando de construir una calculadora, superaría cada paso del cálculo por separado (cada número u operador) en lugar de hacerlo como una sola cadena.
Es demasiado tarde para responder, pero me encontré con la misma situación para evaluar la expresión en Java, podría ayudar a alguien
MVEL
hace una evaluación en tiempo de ejecución de las expresiones, podemos escribir un código java String
para evaluarlo en esto.
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);
Puede echar un vistazo al marco de Symja :
ExprEvaluator util = new ExprEvaluator();
IExpr result = util.evaluate("10-40");
System.out.println(result.toString()); // -> "-30"
Tenga en cuenta que definitivamente se pueden evaluar expresiones más complejas:
// 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
Pruebe el siguiente código de muestra utilizando el motor Javascript de JDK1.6 con manejo de inyección de código.
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;
}
}
}
Esto en realidad complementa la respuesta dada por @Boann. Tiene un pequeño error que hace que "-2 ^ 2" dé un resultado erróneo de -4.0. El problema para eso es el punto en el que se evalúa la exponenciación en su. Simplemente mueva la exponenciación al bloque de parseTerm (), y todo estará bien. Eche un vistazo a la siguiente, que es la respuesta de @ Boann ligeramente modificada. La modificación está en los comentarios.
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
es realmente normal y no es un error. Se agrupa como -(2^2)
. Pruébalo en Desmos, por ejemplo. Su código en realidad introduce varios errores. La primera es que ^
ya no se agrupa de derecha a izquierda. En otras palabras, 2^3^2
se supone que se debe agrupar como 2^(3^2)
porque ^
es asociativo correcto, pero sus modificaciones hacen que se agrupe como (2^3)^2
. El segundo es que ^
se supone que tiene mayor prioridad que *
y /
, pero sus modificaciones lo tratan igual. Ver ideone.com/iN2mMa .
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));
}
}
Qué tal algo como esto:
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);
}
}
y hacer lo mismo para cualquier otro operador matemático en consecuencia.
Es posible convertir cualquier cadena de expresión en notación infija a una notación postfix usando el algoritmo de desviación de Djikstra . El resultado del algoritmo puede servir como entrada para el algoritmo de postfix con retornos del resultado de la expresión.
Escribí un artículo al respecto aquí, con una implementación en Java
Otra opción más: https://github.com/stefanhaustein/expressionparser
He implementado esto para tener una opción simple pero flexible que permita ambos:
El TreeBuilder vinculado anteriormente es parte de un paquete de demostración de CAS que realiza una derivación simbólica. También hay un ejemplo de intérprete BÁSICO y he comenzado a construir un intérprete TypeScript usándolo.
Una clase de Java que puede evaluar expresiones matemáticas:
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) {
if (expression.startsWith("(") && expression.endsWith(")")) {
return calc(expression.substring(1, expression.length() - 1));
}
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)))"));
}
}
Se puede usar una biblioteca externa como RHINO o NASHORN para ejecutar JavaScript. Y JavaScript puede evaluar una fórmula simple sin dividir la cadena. Tampoco tiene impacto en el rendimiento si el código está bien escrito. A continuación se muestra un ejemplo con 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 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; }
//.........
}