Обработка математических уравнений в php - PullRequest
8 голосов
/ 26 октября 2010

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

x + 5

1 - x/2

(x/3) * (56/13)

Они хранятся в виде строк в базе данных.Когда они получены, мне нужно заменить число «x» на число и проверить значение уравнения.

Как я могу это сделать?

Я подумывал написать парсер для деконструкции строк.и превратить их в уравнения, однако это звучит дорого и проблематично.Другой вариант - пропустить их через eval (но я не большой поклонник использования eval, если смогу помочь).

Есть идеи?

ОБНОВЛЕНИЕ: Мне тоже нужно бытьвозможность получить логическое значение чего-то вроде "(x> 5)".Это невозможно с evalMath

ОБНОВЛЕНИЕ 2: мне нужно запустить лотов из них в секунду.Я искал eval в php, но не могу заставить его вернуть логическое значение для (5> 4), однако я заметил, что js сделает это ... возможно, я должен исследовать node.js ...

UPDATE3: После некоторого удовольствия, попробовав node.js (и заставив его работать), я вернулся и получил eval для работы в PHP: Может ли php eval возвращать логическое значение?

Так что я пойду с eval с очень и очень хардкорным фильтром при вводе пользователем.

Ответы [ 8 ]

12 голосов
/ 26 октября 2010

Мой стандартный ответ на этот вопрос, когда он возникает:

Не используйте eval (особенно если вы говорите, что это пользовательский ввод) и не изобретайте колесо заново, написав собственный анализатор формул.

Взгляните на класс evalMath в PHPClasses. Он должен делать все, что вы перечислили здесь.

EDIT

re: К сожалению, evalMath не обрабатывает такие вещи, как (x> 5)

изменить строки 177-179 на

$ops   = array('+', '-', '*', '/', '^', '_', '>', '<', '=');
$ops_r = array('+'=>0,'-'=>0,'*'=>0,'/'=>0,'^'=>0, '>' => 0, '<' => 0, '=' => 0); // right-associative operator?
$ops_p = array('+'=>0,'-'=>0,'*'=>1,'/'=>1,'_'=>1,'^'=>2, '>' => 0, '<' => 0, '=' => 0); // operator precedence

изменить строку 184 на

if (preg_match("/[^\w\s+*^\/()\.,-<>=]/", $expr, $matches)) { // make sure the characters are all good

добавить

case '>':
     $stack->push($op1 > $op2); break;
case '<':
     $stack->push($op1 < $op2); break;
case '=':
     $stack->push($op1 == $op2); break;

после строки 321

и evalMath теперь будет обрабатывать (x> 5), (x <5) или (x = 5) </p>

// instantiate a new EvalMath
$m = new EvalMath;
$m->suppress_errors = true;
// set the value of x
$m->evaluate('x = 3');
var_dump($m->evaluate('y = (x > 5)'));

Далее Редактировать

Пропущена строка 307, которую следует изменить следующим образом:

if (in_array($token, array('+', '-', '*', '/', '^', '>', '<', '='))) {
4 голосов
/ 26 октября 2010

Eval - это не зло !!!!!

Да, это может привести к полной загрузке вашей системы, если вы напишете плохой код - но последние версии PHP могут анализировать недопустимое выражение без сбоя всего скрипта.И есть много других способов разоблачения вашей системы путем написания плохого кода.

Это просто оставляет возможность атак внедрения кода - этого можно легко избежать, выполнив preg_replace для каждого элемента, который не является безопасным символом (т.е.0 .... 9, (,), +, -, *, /, ^,.)

1 голос
/ 27 октября 2010

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

1 голос
/ 26 октября 2010

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

1 - x/2

становится

Array
(
    [0] => -
    [1] => 1
    [2] => Array
        (
            [0] => /
            [1] => x
            [2] => 2
        )
)

Писать синтаксический анализатор немного сложно, но оценить проанализированную формулу действительно просто.

0 голосов
/ 20 сентября 2017

Использование функции eval очень опасно, когда вы не можете контролировать строковый аргумент.

Попробуйте Matex для безопасного вычисления математических формул.Он также поддерживает переменные и пользовательские функции.

0 голосов
/ 26 октября 2010

Зависит ...

Какую сложность он примет? Потому что для общих математических уравнений (наподобие тех, что вы опубликовали) я не вижу особых проблем в написании парсера. Основной проблемный вопрос заключался бы в округлении чисел и правильной круглой скобке.

Но если уравнения будут принимать "продвинутые" входные данные, такие как {[()]}, или X², X³, или получение дальнейшего, дифференциального исчисления и математики в колледже, то все может сойти с ума.

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

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

0 голосов
/ 26 октября 2010

Eval ()

Зависит от того, что вы должны сделать, но в любом случае самый дешевый способ сделать это - использовать функцию замены для переменных, а затем запустить выражение с помощью eval ().
Конечно, сначала нужно убедиться, что ваши формулы имеют синтаксис php.
Хорошо то, что вы можете использовать любую математическую функцию, поддерживаемую php, плохо то, что eval () никогда не бывает приятно использовать :)

PHPClasses

Другой хороший вариант - серфинг в Интернете, пока вы не найдете парсер: P
http://www.phpclasses.org/package/2695-PHP-Safely-evaluate-mathematical-expressions.html

0 голосов
/ 26 октября 2010

Даже если вы пройдете через eval, вам придется заменить x на некоторое число. Стратегия, которую я бы выбрал, - это передать значение для x и посмотреть, что является оцененным значением. Если оно больше 0, я бы попробовал меньшее число, а если оно меньше 0, я бы попытался рекурсивно увеличить большее число, пока оно не удовлетворит допустимый предел ошибки (<> 0,001%).

...