Sto cercando di scrivere una routine Java per valutare semplici espressioni matematiche da String
valori come:
"5+3"
"10-40"
"10*3"
Voglio evitare molte dichiarazioni if-then-else. Come posso fare questo?
Sto cercando di scrivere una routine Java per valutare semplici espressioni matematiche da String
valori come:
"5+3"
"10-40"
"10*3"
Voglio evitare molte dichiarazioni if-then-else. Come posso fare questo?
Risposte:
Con JDK1.6, è possibile utilizzare il motore Javascript incorporato.
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();");
- scriverà un file via JavaScript nella (per impostazione predefinita) nella directory corrente del programma
Ho scritto questo eval
metodo per le espressioni aritmetiche per rispondere a questa domanda. Fa addizione, sottrazione, moltiplicazione, divisione, esponenziazione (usando il ^
simbolo) e alcune funzioni di base come sqrt
. Supporta il raggruppamento utilizzando (
... )
e ottiene la precedenza dell'operatore e le regole di associatività corrette.
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();
}
Esempio:
System.out.println(eval("((4 - 2^3 + 1) * -sqrt(3*3+4*4)) / 2"));
Uscita: 7.5 (che è corretta)
Il parser è un parser di discesa ricorsivo , quindi utilizza internamente metodi di analisi separati per ciascun livello di precedenza dell'operatore nella sua grammatica. L'ho tenuto breve, quindi è facile da modificare, ma ecco alcune idee con cui potresti volerlo espandere:
variabili:
Il bit del parser che legge i nomi per le funzioni può essere facilmente modificato per gestire anche le variabili personalizzate, cercando i nomi in una tabella delle variabili passata al eval
metodo, ad esempio a Map<String,Double> variables
.
Raccolta e valutazione separate:
E se, avendo aggiunto il supporto per le variabili, volessi valutare la stessa espressione milioni di volte con variabili modificate, senza analizzarle ogni volta? È possibile. Definire innanzitutto un'interfaccia da utilizzare per valutare l'espressione precompilata:
@FunctionalInterface
interface Expression {
double eval();
}
Ora cambia tutti i metodi che restituiscono double
s, quindi invece restituiscono un'istanza di tale interfaccia. La sintassi lambda di Java 8 funziona benissimo per questo. Esempio di uno dei metodi modificati:
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;
}
}
}
Ciò crea un albero ricorsivo di Expression
oggetti che rappresentano l'espressione compilata (un albero di sintassi astratto ). Quindi puoi compilarlo una volta e valutarlo ripetutamente con valori diversi:
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());
}
}
Diversi tipi di dati:
Invece double
, potresti cambiare il valutatore per usare qualcosa di più potente BigDecimal
, o una classe che implementa numeri complessi o numeri razionali (frazioni). Potresti persino usare Object
, permettendo un mix di tipi di dati nelle espressioni, proprio come un vero linguaggio di programmazione. :)
Tutto il codice in questa risposta è stato rilasciato al pubblico dominio . Divertiti!
double x = parseTerm();
valuta l'operatore sinistro, dopo questo for (;;) {...}
valuta le operazioni successive del livello di ordine effettivo (addizione, sottrazione). La stessa logica è e nel metodo parseTerm. Il parseFactor non ha un livello successivo, quindi ci sono solo valutazioni di metodi / variabili o in caso di parantesi - valuta la sottoespressione. Il boolean eat(int charToEat)
metodo controlla l'uguaglianza dell'attuale carattere del cursore con il carattere charToEat, se uguale restituisce vero e sposta il cursore sul carattere successivo, uso il nome 'accetta' per questo.
Per il mio progetto universitario, stavo cercando un parser / valutatore che supportasse sia le formule di base che le equazioni più complicate (in particolare gli operatori iterati). Ho trovato una libreria open source molto bella per JAVA e .NET chiamata mXparser. Fornirò alcuni esempi per dare un'idea della sintassi, per ulteriori istruzioni visitare il sito Web del progetto (in particolare la sezione tutorial).
https://mathparser.org/mxparser-tutorial/
E alcuni esempi
1 - Furmula semplice
Expression e = new Expression("( 2 + 3/4 + sin(pi) )/2");
double v = e.calculate()
2 - Argomenti e costanti definiti dall'utente
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 - Funzioni definite dall'utente
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 - Iterazione
Expression e = new Expression("sum( i, 1, 100, sin(i) )");
double v = e.calculate()
Trovato di recente: nel caso in cui si desideri provare la sintassi (e vedere il caso d'uso avanzato) è possibile scaricare l' app Calcolatrice scalare basata su mXparser.
I migliori saluti
QUI è un'altra libreria open source su GitHub chiamata EvalEx.
A differenza del motore JavaScript, questa libreria è focalizzata nella valutazione delle sole espressioni matematiche. Inoltre, la libreria è estensibile e supporta l'uso di operatori booleani e parentesi.
Puoi anche provare l' interprete BeanShell :
Interpreter interpreter = new Interpreter();
interpreter.eval("result = (7+21*6)/(32-27)");
System.out.println(interpreter.get("result"));
È possibile valutare facilmente le espressioni se l'applicazione Java accede già a un database, senza utilizzare altri JAR.
Alcuni database richiedono l'uso di una tabella fittizia (ad esempio, la tabella "doppia" di Oracle) e altri ti permetteranno di valutare le espressioni senza "selezionare" da nessuna tabella.
Ad esempio, in Sql Server o Sqlite
select (((12.10 +12.0))/ 233.0) amount
e in Oracle
select (((12.10 +12.0))/ 233.0) amount from dual;
Il vantaggio dell'utilizzo di un DB è che è possibile valutare più espressioni contemporaneamente. Inoltre, la maggior parte dei DB ti permetterà di usare espressioni molto complesse e avrà anche un numero di funzioni extra che possono essere chiamate come necessario.
Tuttavia, le prestazioni potrebbero risentire della necessità di valutare singolarmente molte singole espressioni, in particolare quando il DB si trova su un server di rete.
Quanto segue risolve il problema delle prestazioni in una certa misura, utilizzando un database in memoria Sqlite.
Ecco un esempio funzionante completo in 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();
Naturalmente è possibile estendere il codice sopra per gestire più calcoli contemporaneamente.
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount, (1+100)/20.0 amount2");
Questo articolo discute vari approcci. Ecco i 2 approcci chiave menzionati nell'articolo:
Consente script che includono riferimenti a oggetti 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();
}
}
Un altro modo è quello di usare Spring Expression Language o SpEL che fa molto di più insieme alla valutazione delle espressioni matematiche, quindi forse leggermente eccessivo. Non è necessario utilizzare il framework Spring per utilizzare questa libreria di espressioni poiché è autonoma. Copia di esempi dalla documentazione di 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
Leggi altri esempi concisi di SpEL qui e i documenti completi qui
se lo implementeremo, allora possiamo usare l'algoritmo seguente: -
Mentre ci sono ancora token da leggere,
1.1 Ottieni il prossimo token. 1.2 Se il token è:
1.2.1 Un numero: spingilo nello stack di valori.
1.2.2 Una variabile: ottieni il suo valore e spingi nello stack di valori.
1.2.3 Una parentesi sinistra: spingerla sulla pila dell'operatore.
1.2.4 Una parentesi corretta:
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 operatore (chiamalo 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.
Mentre lo stack dell'operatore non è vuoto, 1 Estrarre l'operatore dallo stack dell'operatore. 2 Pop lo stack di valori due volte, ottenendo due operandi. 3 Applicare l'operatore agli operandi, nell'ordine corretto. 4 Spingere il risultato sulla pila di valori.
A questo punto lo stack dell'operatore dovrebbe essere vuoto e lo stack dei valori dovrebbe contenere un solo valore, che è il risultato finale.
Questa è un'altra alternativa interessante https://github.com/Shy-Ta/expression-evaluator-demo
L'utilizzo è molto semplice e porta a termine il lavoro, ad esempio:
ExpressionsEvaluator evalExpr = ExpressionsFactory.create("2+3*4-6/2");
assertEquals(BigDecimal.valueOf(11), evalExpr.eval());
Penso che in qualunque modo tu faccia questo implicherà molte affermazioni condizionali. Ma per singole operazioni come nei tuoi esempi potresti limitarlo a 4 se istruzioni con qualcosa di simile
String math = "1+4";
if (math.split("+").length == 2) {
//do calculation
} else if (math.split("-").length == 2) {
//do calculation
} ...
Diventa molto più complicato quando vuoi occuparti di più operazioni come "4 + 5 * 6".
Se stai cercando di costruire una calcolatrice, farei più rapidamente il passaggio di ciascuna sezione del calcolo separatamente (ogni numero o operatore) anziché come singola stringa.
È troppo tardi per rispondere, ma mi sono imbattuto nella stessa situazione per valutare l'espressione in java, potrebbe aiutare qualcuno
MVEL
fa la valutazione runtime delle espressioni, possiamo scrivere un codice Java String
per farlo valutare in questo.
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);
Potresti dare un'occhiata al framework Symja :
ExprEvaluator util = new ExprEvaluator();
IExpr result = util.evaluate("10-40");
System.out.println(result.toString()); // -> "-30"
Si noti che è possibile valutare in modo definitivo espressioni più complesse:
// 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
Prova il seguente codice di esempio utilizzando il motore Javascript di JDK1.6 con la gestione dell'iniezione di codice.
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;
}
}
}
Questo in realtà completa la risposta data da @Boann. Ha un leggero bug che fa sì che "-2 ^ 2" dia un risultato errato di -4.0. Il problema per questo è il punto in cui viene valutata l'espiazione nella sua. Basta spostare l'esponente nel blocco di parseTerm () e andrà tutto bene. Dai un'occhiata al seguito, che è la risposta di @ Boann leggermente modificata. La modifica è nei commenti.
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
è in realtà normale e non un bug. Viene raggruppato come -(2^2)
. Provalo su Desmos, per esempio. Il codice in realtà introduce diversi bug. Il primo è che ^
non raggruppa più da destra a sinistra. In altre parole, 2^3^2
dovrebbe raggrupparsi come 2^(3^2)
perché ^
è associativo giusto, ma le tue modifiche lo rendono simile a un gruppo (2^3)^2
. Il secondo è che ^
dovrebbe avere una precedenza più alta di *
e /
, ma le tue modifiche lo trattano allo stesso modo. Vedi 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));
}
}
Che ne dici di qualcosa del genere:
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 fare la stessa cosa per ogni altro operatore matematico di conseguenza ...
È possibile convertire qualsiasi stringa di espressione nella notazione infissa in una notazione postfissa usando l' algoritmo di shunting yard di Djikstra . Il risultato dell'algoritmo può quindi servire come input per l' algoritmo postfix con restituisce il risultato dell'espressione.
Ho scritto un articolo a riguardo qui, con un'implementazione in Java
Ancora un'altra opzione: https://github.com/stefanhaustein/expressionparser
Ho implementato questo per avere un'opzione semplice ma flessibile per consentire entrambi:
TreeBuilder collegato sopra fa parte di un pacchetto demo CAS che esegue derivazioni simboliche. C'è anche un esempio di interprete BASIC e ho iniziato a costruire un interprete TypeScript usando esso.
Una classe Java in grado di valutare espressioni matematiche:
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)))"));
}
}
Una libreria esterna come RHINO o NASHORN può essere utilizzata per eseguire javascript. E javascript può valutare una formula semplice senza parare la stringa. Nessun impatto sulle prestazioni se il codice è scritto bene. Di seguito è riportato un esempio 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; }
//.........
}