How to evaluate a math expression given in string form?

Я пытаюсь написать подпрограмму Java для оценки простых математических выражений из String значения как:

  1. "5+3"
  2. "10-40"
  3. "10*3"

Я хочу избегать множества утверждений if-then-else. Как я могу это сделать?

28 ответов

Решение

С 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));
    } 
}

Я написал это 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();
}

Пример:

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();
    }
    

    Теперь измените все методы, которые возвращают double s, поэтому вместо этого они возвращают экземпляр этого интерфейса. Лямбда-синтаксис Java 8 отлично подходит для этого. Пример одного из измененных методов:

    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, позволяя смешивать типы данных в выражениях, как настоящий язык программирования.:)


Весь код в этом ответе опубликован в открытом доступе. Повеселись!

Для моего университетского проекта я искал парсер / оценщик, поддерживающий как базовые формулы, так и более сложные уравнения (особенно итерированные операторы). Я нашел очень хорошую библиотеку с открытым исходным кодом для JAVA и.NET, которая называется mXparser. Я приведу несколько примеров, чтобы немного разобраться в синтаксисе, для дальнейших инструкций, пожалуйста, посетите веб-сайт проекта (особенно раздел учебника).

http://mathparser.org/

http://mathparser.org/mxparser-tutorial/

http://mathparser.org/api/

И несколько примеров

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()

С наилучшими пожеланиями

Правильный способ решить это с помощью лексера и парсера. Вы можете написать их простые версии, или на этих страницах также есть ссылки на лексеры и парсеры Java.

Создание парсера с рекурсивным спуском - это действительно хорошее учебное упражнение.

ЗДЕСЬ есть еще одна библиотека с открытым исходным кодом на GitHub под названием EvalEx.

В отличие от движка JavaScript эта библиотека ориентирована только на оценку математических выражений. Кроме того, библиотека является расширяемой и поддерживает использование логических операторов, а также скобок.

Вы также можете попробовать интерпретатор BeanShell:

Interpreter interpreter = new Interpreter();
interpreter.eval("result = (7+21*6)/(32-27)");
System.out.println(interpreter.get("result"));

Вы можете легко оценить выражения, если ваше 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");

Другой способ - использовать Spring Expression Language или SpEL, который делает намного больше вместе с оценкой математических выражений, поэтому может быть немного излишним. Вам не нужно использовать Spring Framework, чтобы использовать эту библиотеку выражений, поскольку она автономна. Копирование примеров из документации 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

Прочитайте более краткие примеры SpEL здесь и полные документы здесь

В этой статье рассматриваются 3 различных подхода, один из которых - JEXL от Apache и позволяет создавать сценарии, содержащие ссылки на объекты Java.

Если мы собираемся реализовать это, то мы можем использовать следующий алгоритм:

  1. Пока еще есть токены для чтения,

    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.
    
  2. Пока стек операторов не пуст, 1 вытолкните оператор из стека операторов. 2 Дважды вытолкните стек значений, получив два операнда. 3 Примените оператор к операндам в правильном порядке. 4 Вставьте результат в стек значений.

  3. На этом этапе стек операторов должен быть пустым, а в стеке значений должно быть только одно значение, что является конечным результатом.

Это еще одна интересная альтернатива https://github.com/Shy-Ta/expression-evaluator-demo

Использование очень простое и выполняет свою работу, например:

  ExpressionsEvaluator evalExpr = ExpressionsFactory.create("2+3*4-6/2");  
  assertEquals(BigDecimal.valueOf(11), evalExpr.eval()); 

Кажется, что JEP должен сделать работу

Я думаю, что когда бы вы ни делали это, это будет включать много условных утверждений. Но для отдельных операций, как в ваших примерах, вы можете ограничить его до 4, если операторы с чем-то вроде

String math = "1+4";

if (math.split("+").length == 2) {
    //do calculation
} else if (math.split("-").length == 2) {
    //do calculation
} ...

Это становится намного сложнее, когда вы хотите иметь дело с несколькими операциями, такими как "4+5*6".

Если вы пытаетесь построить калькулятор, я бы предпочел передавать каждую часть вычисления отдельно (каждый номер или оператор), а не как одну строку.

Попробуйте следующий пример кода, используя Javascript движок JDK1.6 с обработкой внедрения кода.

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;
    }
 }
}

Слишком поздно, чтобы ответить, но я столкнулся с той же ситуацией, чтобы оценить выражение в 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);
import java.util.*;
StringTokenizer st;
int ans;

public class check { 
   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; }
   //.........
}

Это на самом деле дополняет ответ, данный @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();
}

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) {

        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)))"));

    }

}

Вы могли бы взглянуть на платформу 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
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));
}

}

Внешняя библиотека, такая как 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);
}

Как насчет чего-то вроде этого:

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);
  }         
}

и сделать то же самое для каждого другого математического оператора соответственно..

Еще один вариант: https://github.com/stefanhaustein/expressionparser

Я реализовал это, чтобы иметь простой, но гибкий вариант, позволяющий:

TreeBuilder, связанный выше, является частью демонстрационного пакета CAS, который выполняет символическое создание. Существует также пример интерпретатора BASIC, и я начал создавать интерпретатор TypeScript, используя его.

Можно преобразовать любую строку выражения в инфиксной записи в постфиксную запись с помощью алгоритма шунтирования ярда Джикстры. Результат алгоритма может затем служить в качестве входных данных для алгоритма постфикса с возвращением результата выражения.

Я написал статью об этом здесь, с реализацией в Java

Если кому-то нужны точные десятичные числа, я сделал модификацию, которая использует вместоdouble.

Меня вдохновил ответ Боанна, и я изменил его, чтобы использовать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);

Я использую это. Но если в java есть методы получше, дайте мне знать.

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(), и я обновил код в приведенной выше ссылке.

public class StringCalculator {

    public static void main(String[] args) {

        String eval = "2+3*2/2+2*5/5*5";

        System.out.println(calculator(eval));
    }

    public static int calcMulAndDiv(String val){

        String nos[] = val.split("\\D+");
        String opr[] = val.split("\\d+");
        int res = Integer.parseInt(nos[0]);

        for(int i = 1; i< opr.length ;i++){

            if(opr[i].equals("*")){
                res = res * Integer.parseInt(nos[i]);
            }
            else if(opr[i].equals("/")){
                res = res / Integer.parseInt(nos[i]);
            }

        }

        return res;
    }

    public static int calculator(String val){

        String nos[] = val.split("[+-]");
        String operators = val.replaceAll("[^+-]","");
        char opr[] = operators.toCharArray();
        int result = 0;

        if(nos[0].contains("*") || nos[0].contains("*")){
            result = calcMulAndDiv(nos[0]);
        }else{
            result = Integer.parseInt(nos[0]);
        }

        for(int i = 0 ; i < opr.length ; i++){

            if(opr[i] == '+'){
                if(nos[i+1].contains("*") || nos[i+1].contains("*")){
                    result = result + calcMulAndDiv(nos[i+1]);
                }else{
                    result = result + Integer.parseInt(nos[i+1]);
                }
            }
            else if(opr[i] == '-'){
                if(nos[i+1].contains("*") || nos[i+1].contains("*")){
                    result = result + calcMulAndDiv(nos[i+1]);
                }else{
                    result = result - Integer.parseInt(nos[i+1]);
                }
            }

        }

        return result;
    }
}
Другие вопросы по тегам