Как разобрать математическое выражение, данное в виде строки, и вернуть число?

Есть ли в Java способ получить результат из этого математического выражения:

String code = "5+4*(7-15)";

С другой стороны, как лучше всего разобрать арифметическое выражение?


person Martijn Courteaux    schedule 16.09.2009    source источник


Ответы (10)


Вы можете передать его в BeanShell bsh.Interpreter, примерно так:

Interpreter interpreter = new Interpreter();
interpreter.eval("result = 5+4*(7-15)");
System.out.println(interpreter.get("result"));

Вы должны убедиться, что строка, которую вы оцениваете, получена из надежного источника и с обычными мерами предосторожности, но в противном случае она будет работать сразу.

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

person Nick Holt    schedule 16.09.2009
comment
Хорошая идея, но проблематичная с ненадежным вводом, так как она допускает внедрение сценария (см. Другой мой комментарий). - person sleske; 16.09.2009
comment
@sleske: да, вам определенно придется быть осторожным с атаками инъекций, если строка вводится пользователем. - person Nick Holt; 16.09.2009
comment
Также теперь можно использовать встроенный интерпретатор JavaScript (Rhino). Хотя кажется более сложным получить eval. И все же есть риск для безопасности ... - person PhiLho; 16.09.2009
comment
@PhiLho: BeanShell - это просто то, что я использовал несколько лет назад, но я согласен, что что-то на основе javax.script API (я думаю, что Rhino) было бы лучше. - person Nick Holt; 16.09.2009
comment
+1 за бобовую скорлупу, являющуюся частью JRE - person Thorbjørn Ravn Andersen; 16.09.2009
comment
Что бы это вернуло, если бы выражение было 2/3? Вернет ли он 0, как это делает Java? Вы можете вставить приведение типа впереди, но как насчет (2/3) + (1/2)? - person dreeves; 06.05.2010
comment
Попытка использовать beanshell ... Как я могу гарантировать, что результат всегда будет в формате DOUBLE? - person marcolopes; 22.04.2012

Недавно я разработал парсер выражений и выпустил его под лицензией apache. вы можете скачать его на http://projects.congrace.de/exp4j/index.html

надеюсь, что это помогло

person fasseg    schedule 09.03.2011
comment
Красиво, невероятно просто в использовании и отлично подходит для моих нужд. - person Kevin Cooper; 13.10.2012
comment
Я не буду использовать это, потому что мне действительно нужно анализировать одно-единственное выражение во всем моем приложении (из файла конфигурации), но если бы у меня было больше и я не мог бы обойти необходимость, я бы обязательно использовал это! Я только что взглянул на это, и он выглядит потрясающе (поддерживаются даже пользовательские функции!). Отличная работа! - person Igor; 15.04.2014

Вы можете использовать класс ScriptEngine и оценивать его как строку javascript

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");        
Object result = engine.eval("5+4*(7-15)");

Действительно, вы должны знать, что результат следующей инструкции в javascript:

   eval('var aa=5+4*(7-15)')
   aa // -27

Может быть способ получше, но этот работает.

person Abdennour TOUMI    schedule 21.04.2014
comment
Спасибо, я предпочитаю не добавлять зависимости, когда мне это действительно не нужно: D - person shbi; 06.10.2015
comment
@shbi: это не внешняя библиотека, это встроенная javax.script.* - person Abdennour TOUMI; 20.06.2017
comment
Понятно, но поскольку это выражение считывается из внешнего файла, JS-движок, оценивающий произвольный код, не подходит. - person shbi; 07.07.2017

Вероятно, не так просто, как вы надеетесь!

Но, возможно, вы могли бы использовать javax.script.ScriptEngine и, например, рассматривать строку как выражение ECMAScript?

Взгляните на: Сценарии для Платформа Java.

person Andy    schedule 16.09.2009
comment
Это довольно опасно, поскольку допускает внедрение сценария (аналогично SQL-инъекции). Действовать с осторожностью. - person sleske; 16.09.2009
comment
Хорошая точка зрения. Полагаю, это зависит от источника выражений. - person Andy; 16.09.2009
comment
Регулярное выражение можно использовать для удаления всех нематематических символов из входной строки. Защитит ли это приложение от внедрения сценария? (Не планирую использовать это, но просто наткнулся на это, и это вызвало у меня любопытство) - person FThompson; 01.12.2012

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

person soulmerge    schedule 16.09.2009

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

https://mathparser.org/

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

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

Пример 1 - простая формула

Expression e = new Expression("2+3");
double v = e.calculate();

Пример 2 - встроенная функция

Expression e = new Expression("2+sin(3)");
double v = e.calculate();

Пример 3 - встроенные константы

Expression e = new Expression("2+sin(pi)");
double v = e.calculate();

Пример 4 - аргументы и константы, определяемые пользователем

Argument x = new Argument("x = 5");
Constant a = new Constant("a = 2 + sin(3)");
Expression e = new Expression("a + x^2", x, a);
double v1 = e.calculate();
x.setArgumentValue(10);
double v2 = e.calculate();

Пример 5 - определяемые пользователем функции

Function f = new Function("f(x,y) = x^2 + cos(y)");
Expression e = new Expression("f(10,pi) - 3", f);
double v = e.calculate();

Пример 6 - определяемая пользователем рекурсия

Function factorial = new Function("fact(n) = if( n > 0; n*fact(n-1); 1)");
Expression e = new Expression("fact(10) - 10!", factorial);
double v = e.calculate();

Найдено недавно - если вы хотите попробовать синтаксис (и увидеть расширенный вариант использования), вы можете загрузить Скалярный калькулятор приложение, работающее на mXparser.

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

LK

person Leroy Kegan    schedule 31.03.2016

В Java SDK нет прямой поддержки для этого.

Вам придется либо реализовать его самостоятельно (возможно, с помощью генератора синтаксического анализатора, такого как JavaCC), либо использовать существующую библиотеку.

Один вариант - JEP (коммерческий), другой - JEval (бесплатное программное обеспечение).

person sleske    schedule 16.09.2009

Вы можете использовать этот проект

Как пользоваться:

double result = 0;
String code = "5+4*(7-15)";
try {
    Expr expr = Parser.parse(code);
    result = expr.value();
} catch (SyntaxException e) {
    e.printStackTrace();
}
System.out.println(String.format("Result: %.04f", result));
person Jc Miñarro    schedule 21.01.2014

Есть инструмент с открытым исходным кодом под названием formula4j, который выполняет эту работу.

Чтобы взять ваше примерное выражение, оно будет оцениваться следующим образом с помощью formula4j:

Formula formula = new Formula("5+4*(7-15)");

Decimal answer = formula.getAnswer(); //-27

person John    schedule 15.02.2013
comment
Обновил ссылку. Инструмент formula4j теперь имеет открытый исходный код. - person John; 06.11.2020

person    schedule
comment
Это не сработает даже с простым примером в вопросе. - person Mat; 19.12.2014
comment
Хотя у меня работает хорошо. - person Semioniy; 19.12.2014
comment
Может сработать для вас, но не отвечает на вопрос. - person Mat; 19.12.2014
comment
Всегда есть лучший способ сделать что-л. - Лучшего способа для чего-либо нет. Надеюсь, вы найдете это полезным. - person Semioniy; 19.12.2014
comment
это не сработает при a + b * c, в этом коде нет приоритета между операторами ... - person Mohsen_Fatemi; 23.01.2017