Estou tentando escrever uma rotina Java para avaliar expressões matemáticas simples a partir de Stringvalores como:
"5+3""10-40""10*3"
Eu quero evitar muitas instruções if-then-else. Como posso fazer isso?
Estou tentando escrever uma rotina Java para avaliar expressões matemáticas simples a partir de Stringvalores como:
"5+3""10-40""10*3"Eu quero evitar muitas instruções if-then-else. Como posso fazer isso?
Respostas:
Com o JDK1.6, você pode usar o mecanismo Javascript interno.
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();");- gravará um arquivo via JavaScript no diretório atual do programa
Eu escrevi esse evalmétodo para expressões aritméticas para responder a essa pergunta. Faz adição, subtração, multiplicação, divisão, exponenciação (usando o ^símbolo) e algumas funções básicas, como sqrt. Ele suporta o agrupamento usando (... )e corrige as regras de precedência e associatividade do operador .
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();
}
Exemplo:
System.out.println(eval("((4 - 2^3 + 1) * -sqrt(3*3+4*4)) / 2"));
Saída: 7,5 (correta)
O analisador é um analisador de descida recursiva ; portanto, usa internamente métodos de análise separados para cada nível de precedência do operador em sua gramática. Eu o mantive curto, para facilitar a modificação, mas aqui estão algumas idéias que você pode querer expandi-lo:
Variáveis:
O bit do analisador que lê os nomes das funções também pode ser facilmente alterado para manipular variáveis personalizadas, procurando nomes em uma tabela de variáveis passada para o evalmétodo, como a Map<String,Double> variables.
Compilação e avaliação separadas:
E se, tendo adicionado suporte para variáveis, você quisesse avaliar a mesma expressão milhões de vezes com variáveis alteradas, sem analisá-la toda vez? É possível. Primeiro, defina uma interface a ser usada para avaliar a expressão pré-compilada:
@FunctionalInterface
interface Expression {
double eval();
}
Agora altere todos os métodos que retornam doubles; portanto, eles retornam uma instância dessa interface. A sintaxe lambda do Java 8 funciona muito bem para isso. Exemplo de um dos métodos alterados:
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;
}
}
}
Isso cria uma árvore recursiva de Expressionobjetos que representam a expressão compilada (uma árvore de sintaxe abstrata ). Em seguida, você pode compilá-lo uma vez e avaliá-lo repetidamente com valores diferentes:
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());
}
}
Tipos de dados diferentes:
Em vez de double, você pode alterar o avaliador para usar algo mais poderoso como BigDecimal, ou uma classe que implemente números complexos ou números racionais (frações). Você pode até usar Object, permitindo alguma mistura de tipos de dados em expressões, como uma linguagem de programação real. :)
Todo o código nesta resposta foi liberado para o domínio público . Diverta-se!
double x = parseTerm(); avalia o operador esquerdo, depois for (;;) {...}avalia as operações sucessivas do nível de pedido real (adição, subtração). A mesma lógica é e no método parseTerm. O parseFactor não possui próximo nível, portanto, existem apenas avaliações de métodos / variáveis ou, no caso de parênteses - avaliar subexpressão. O boolean eat(int charToEat)método verifica a igualdade do caractere atual do cursor com o caractere charToEat, se igual retornar true e mover o cursor para o próximo caractere, eu uso o nome 'accept' para ele.
A maneira correta de resolver isso é com um lexer e um analisador . Você mesmo pode escrever versões simples delas, ou essas páginas também possuem links para analisadores e analisadores Java.
Criar um analisador de descida recursiva é realmente um bom exercício de aprendizado.
Para o meu projeto universitário, eu estava procurando por um analisador / avaliador que suporta fórmulas básicas e equações mais complicadas (especialmente operadores iterados). Eu achei uma biblioteca de código aberto muito boa para JAVA e .NET chamada mXparser. Vou dar alguns exemplos para entender a sintaxe. Para obter mais instruções, visite o site do projeto (especialmente a seção do tutorial).
https://mathparser.org/mxparser-tutorial/
E alguns exemplos
1 - Férmula simples
Expression e = new Expression("( 2 + 3/4 + sin(pi) )/2");
double v = e.calculate()
2 - Argumentos e constantes definidas pelo usuário
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 - Funções definidas pelo usuário
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 - Iteração
Expression e = new Expression("sum( i, 1, 100, sin(i) )");
double v = e.calculate()
Encontrado recentemente - caso queira experimentar a sintaxe (e veja o caso de uso avançado), você pode fazer o download do aplicativo Calculadora Escalar, desenvolvido com mXparser.
Cumprimentos
O HERE é outra biblioteca de código aberto no GitHub chamada EvalEx.
Diferentemente do mecanismo JavaScript, esta biblioteca é focada na avaliação apenas de expressões matemáticas. Além disso, a biblioteca é extensível e suporta o uso de operadores booleanos, bem como parênteses.
Você também pode tentar o intérprete BeanShell :
Interpreter interpreter = new Interpreter();
interpreter.eval("result = (7+21*6)/(32-27)");
System.out.println(interpreter.get("result"));
Você pode avaliar expressões facilmente se seu aplicativo Java já acessar um banco de dados, sem usar outros JARs.
Alguns bancos de dados exigem o uso de uma tabela fictícia (por exemplo, a tabela "dupla" do Oracle) e outros permitem avaliar expressões sem "selecionar" em qualquer tabela.
Por exemplo, no Sql Server ou Sqlite
select (((12.10 +12.0))/ 233.0) amount
e no Oracle
select (((12.10 +12.0))/ 233.0) amount from dual;
A vantagem de usar um banco de dados é que você pode avaliar muitas expressões ao mesmo tempo. Além disso, a maioria dos bancos de dados permitirá que você use expressões altamente complexas e também terá várias funções extras que podem ser chamadas conforme necessário.
No entanto, o desempenho pode sofrer se muitas expressões únicas precisarem ser avaliadas individualmente, principalmente quando o banco de dados estiver localizado em um servidor de rede.
A seguir, aborda o problema de desempenho, até certo ponto, usando um banco de dados Sqlite na memória.
Aqui está um exemplo completo de trabalho em 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();
Obviamente, você pode estender o código acima para lidar com vários cálculos ao mesmo tempo.
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount, (1+100)/20.0 amount2");
Este artigo discute várias abordagens. Aqui estão as 2 principais abordagens mencionadas no artigo:
Permite scripts que incluem referências 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();
}
}
Outra maneira é usar o Spring Expression Language ou SpEL, que faz muito mais junto com a avaliação de expressões matemáticas, portanto, talvez um pouco exagero. Você não precisa usar a estrutura Spring para usar esta biblioteca de expressões, pois é independente. Copiando exemplos da documentação do 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
Leia mais exemplos concisos de SpEL aqui e os documentos completos aqui
se vamos implementá-lo, podemos usar o algoritmo abaixo: -
Embora ainda haja tokens para serem lidos,
1.1 Obtenha o próximo token. 1.2 Se o token for:
1.2.1 Um número: empurre-o para a pilha de valores.
1.2.2 Uma variável: obtenha seu valor e empurre para a pilha de valores.
1.2.3 Um parêntese esquerdo: empurre-o para a pilha do operador.
1.2.4 Um parêntese direito:
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 Um operador (chame-o 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.Enquanto a pilha do operador não estiver vazia, 1 Retire o operador da pilha do operador. 2 Coloque a pilha de valores duas vezes, obtendo dois operandos. 3 Aplique o operador aos operandos, na ordem correta. 4 Empurre o resultado na pilha de valores.
Nesse ponto, a pilha do operador deve estar vazia e a pilha de valores deve ter apenas um valor, que é o resultado final.
Esta é outra alternativa interessante https://github.com/Shy-Ta/expression-evaluator-demo
O uso é muito simples e faz o trabalho, por exemplo:
ExpressionsEvaluator evalExpr = ExpressionsFactory.create("2+3*4-6/2");
assertEquals(BigDecimal.valueOf(11), evalExpr.eval());
Eu acho que de qualquer maneira que você faça isso, isso envolverá muitas declarações condicionais. Porém, para operações únicas, como nos seus exemplos, você pode limitar a 4 se declarações com algo como
String math = "1+4";
if (math.split("+").length == 2) {
//do calculation
} else if (math.split("-").length == 2) {
//do calculation
} ...
Fica muito mais complicado quando você deseja lidar com várias operações como "4 + 5 * 6".
Se você está tentando construir uma calculadora, seria melhor passar cada seção do cálculo separadamente (cada número ou operador), e não como uma única string.
É tarde demais para responder, mas me deparei com a mesma situação para avaliar a expressão em java, isso pode ajudar alguém
MVELfaz avaliação de expressões em tempo de execução, podemos escrever um código java Stringpara que seja avaliado nisso.
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);
Você pode dar uma olhada na estrutura Symja :
ExprEvaluator util = new ExprEvaluator();
IExpr result = util.evaluate("10-40");
System.out.println(result.toString()); // -> "-30"
Observe que expressões definitivamente mais complexas podem ser avaliadas:
// 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
Experimente o seguinte código de exemplo usando o mecanismo Javascript do JDK1.6 com manipulação de injeção 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;
}
}
}
Na verdade, isso complementa a resposta dada por @Boann. Ele tem um pequeno erro que faz com que "-2 ^ 2" dê um resultado incorreto de -4,0. O problema é o ponto em que a exponenciação é avaliada na dele. Apenas mova a exponenciação para o bloco de parseTerm () e você ficará bem. Dê uma olhada no abaixo, que é a resposta de @ Boann ligeiramente modificada. A modificação está nos comentários.
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é realmente normal, e não um bug. É agrupado como -(2^2). Experimente no Desmos, por exemplo. Seu código realmente apresenta vários bugs. A primeira é que ^não agrupa mais da direita para a esquerda. Em outras palavras, 2^3^2é suposto agrupar como 2^(3^2)porque ^é associativo à direita, mas suas modificações o tornam como grupo (2^3)^2. A segunda é que ^deveria ter maior precedência que *e /, mas suas modificações tratam da mesma forma. Consulte 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));
}
}
Que tal algo como isso:
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);
}
}
e faça o mesmo para todos os outros operadores matemáticos.
É possível converter qualquer string de expressão na notação infix em uma notação postfix usando o algoritmo shunting-yard do Djikstra . O resultado do algoritmo pode então servir como entrada para o algoritmo postfix com retorna o resultado da expressão.
Eu escrevi um artigo sobre isso aqui, com uma implementação em java
Mais uma opção: https://github.com/stefanhaustein/expressionparser
Eu implementei isso para ter uma opção simples, mas flexível, para permitir ambos:
O TreeBuilder vinculado acima faz parte de um pacote de demonstração CAS que faz derivação simbólica. Há também um exemplo de intérprete BASIC e eu comecei a criar um intérprete TypeScript usando-o.
Uma classe Java que pode avaliar expressões 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)))"));
}
}
Biblioteca externa como RHINO ou NASHORN pode ser usada para executar o javascript. E o javascript pode avaliar uma fórmula simples sem separar a string. Sem impacto no desempenho também se o código for escrito corretamente. Abaixo está um exemplo com 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; }
//.........
}