php 如何在数学上评估像“2-1”这样的字符串以产生“1”?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5057320/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 16:41:05  来源:igfitidea点击:

How to mathematically evaluate a string like "2-1" to produce "1"?

phpmathnumbersoperatorseval

提问by Nikola

I was just wondering if PHP has a function that can take a string like 2-1and produce the arithmetic result of it?

我只是想知道 PHP 是否有一个函数可以接受像这样的字符串2-1并产生它的算术结果?

Or will I have to do this manually with explode()to get the values left and right of the arithmetic operator?

或者我是否必须手动执行此操作explode()才能获得算术运算符的左侧和右侧的值?

回答by Jon

I know this question is old, but I came across it last night while searching for something that wasn't quite related, and every single answer here is bad. Not just bad, verybad. The examples I give here will be from a class that I created back in 2005 and spent the past few hours updating for PHP5 because of this question. Other systems do exist, and were around before this question was posted, so it baffles me why every answer here tells you to use eval, when the caution from PHP is:

我知道这个问题很老,但我昨晚在搜索不太相关的东西时遇到了它,这里的每一个答案都很糟糕。不只是坏,非常坏。我在这里给出的示例将来自我在 2005 年创建的一个类,由于这个问题,我花了过去几个小时更新 PHP5。其他系统确实存在,并且在发布这个问题之前就已经存在了,所以eval当 PHP 的警告是:

The eval() language construct is very dangerous because it allows execution of arbitrary PHP code. Its use thus is discouraged. If you have carefully verified that there is no other option than to use this construct, pay special attention not to pass any user provided data into it without properly validating it beforehand.

eval() 语言结构非常危险,因为它允许执行任意 PHP 代码。因此不鼓励使用它。如果您已经仔细确认除了使用此构造没有其他选择,请特别注意不要在没有事先正确验证的情况下将任何用户提供的数据传递给它。

Before I jump in to the example, the places to get the class I will be using is on either PHPClassesor GitHub. Both the eos.class.phpand stack.class.phpare required, but can be combined in to the same file.

在我进入示例之前,获取我将使用的类的位置在PHPClassesGitHub 上。无论是eos.class.phpstack.class.php是必需的,但可以在同一个文件相结合。

The reason for using a class like this is that it includes and infix to postfix(RPN) parser, and then an RPN Solver. With these, you never have to use the evalfunction and open your system up to vulnerabilities. Once you have the classes, the following code is all that is needed to solve a simple (to more complex) equation such as your 2-1example.

使用这样的类的原因是它包含并中缀到 postfix(RPN) 解析器,然后是 RPN 求解器。有了这些,您永远不必使用该eval功能并打开系统漏洞。一旦你有了这些类,下面的代码就是解决一个简单的(到更复杂的)方程(比如你的2-1例子)所需要的全部代码。

require_once "eos.class.php";
$equation = "2-1";
$eq = new eqEOS();
$result = $eq->solveIF($equation);

That's it! You can use a parser like this for most equations, however complicated and nested without ever having to resort to the 'evil eval'.

就是这样!对于大多数方程,您可以使用这样的解析器,无论它多么复杂和嵌套,而不必求助于“邪恶eval”。

Because I really don't want this only only to have my class in it, here are some other options. I am just familiar with my own since I've been using it for 8 years. ^^

因为我真的不希望这只是让我的班级在里面,这里有一些其他的选择。我只是熟悉我自己的,因为我已经使用它 8 年了。^^

Wolfram|Alpha API
Sage
A fairly bad parser
phpdicecalc

Wolfram|Alpha API
Sage
一个相当糟糕的解析器
phpdicecalc

Not quite sure what happened to others that I had found previously - came across another one on GitHub before as well, unfortunately I didn't bookmark it, but it was related to large float operations that included a parser as well.

不太确定我之前发现的其他人发生了什么 - 之前在 GitHub 上也遇到过另一个,不幸的是我没有为它添加书签,但它与包含解析器的大型浮点操作有关。

Anyways, I wanted to make sure an answer to solving equations in PHP on here wasn't pointing all future searchers to evalas this was at the top of a google search. ^^

无论如何,我想确保这里用 PHP 求解方程的答案不会指向所有未来的搜索者,eval因为它位于谷歌搜索的顶部。^^

回答by dynamic

$operation='2-1';
eval("$value = \"$operation\";");

or

或者

$value=eval("return ($op);");

回答by gion_13

This is one of the cases where evalcomes in handy:

这是eval派上用场的情况之一:

$expression = '2 - 1';
eval( '$result = (' . $expression . ');' );
echo $result;

回答by delphist

You can use BC Math arbitrary precision

您可以使用 BC Math 任意精度

echo bcsub(5, 4); // 1
echo bcsub(1.234, 5); // 3
echo bcsub(1.234, 5, 4); // -3.7660

http://www.php.net/manual/en/function.bcsub.php

http://www.php.net/manual/en/function.bcsub.php

回答by AmirG

In thisforum someone made it without eval. Maybe you can try it? Credits to them, I just found it.

这个论坛中,有人在没有eval. 也许你可以试试?感谢他们,我刚刚找到它。

function calculate_string( $mathString )    {
    $mathString = trim($mathString);     // trim white spaces
    $mathString = ereg_replace ('[^0-9\+-\*\/\(\) ]', '', $mathString);    // remove any non-numbers chars; exception for math operators

    $compute = create_function("", "return (" . $mathString . ");" );
    return 0 + $compute();
}

$string = " (1 + 1) * (2 + 2)";
echo calculate_string($string);  // outputs 8  

回答by kurdtpage

Also see this answer here: Evaluating a string of simple mathematical expressions

也可以在这里看到这个答案:评估一串简单的数学表达式

Please note this solution does NOT conform to BODMAS, but you can use brackets in your evaluation string to overcome this.

请注意,此解决方案不符合 BODMAS,但您可以在评估字符串中使用括号来解决此问题。

function callback1($m) {
    return string_to_math($m[1]);
}
function callback2($n,$m) {
    $o=$m[0];
    $m[0]=' ';
    return $o=='+' ? $n+$m : ($o=='-' ? $n-$m : ($o=='*' ? $n*$m : $n/$m));
}
function string_to_math($s){ 
    while ($s != ($t = preg_replace_callback('/\(([^()]*)\)/','callback1',$s))) $s=$t;
    preg_match_all('![-+/*].*?[\d.]+!', "+$s", $m);
    return array_reduce($m[0], 'callback2');
}
echo string_to_match('2-1'); //returns 1

回答by Samir Das

As create_function got deprecated and I was utterly needed an alternative lightweight solution of evaluating string as math. After a couple of hours spending, I came up with following. By the way, I did not care about parentheses as I don't need in my case. I just needed something that conform operator precedence correctly.

随着 create_function 被弃用,我完全需要一个替代的轻量级解决方案,将字符串评估为数学。花了几个小时后,我想出了以下内容。顺便说一句,我不关心括号,因为我不需要。我只需要正确符合运算符优先级的东西。

Update: I have added parentheses support as well. Please check this project Evaluate Math String

更新:我也添加了括号支持。请检查此项目评估数学字符串

function evalAsMath($str) {

   $error = false;
   $div_mul = false;
   $add_sub = false;
   $result = 0;

   $str = preg_replace('/[^\d\.\+\-\*\/]/i','',$str);
   $str = rtrim(trim($str, '/*+'),'-');

   if ((strpos($str, '/') !== false ||  strpos($str, '*') !== false)) {
      $div_mul = true;
      $operators = array('*','/');
      while(!$error && $operators) {
         $operator = array_pop($operators);
         while($operator && strpos($str, $operator) !== false) {
           if ($error) {
              break;
            }
           $regex = '/([\d\.]+)\'.$operator.'(\-?[\d\.]+)/';
           preg_match($regex, $str, $matches);
           if (isset($matches[1]) && isset($matches[2])) {
                if ($operator=='+') $result = (float)$matches[1] + (float)$matches[2];
                if ($operator=='-') $result = (float)$matches[1] - (float)$matches[2]; 
                if ($operator=='*') $result = (float)$matches[1] * (float)$matches[2]; 
                if ($operator=='/') {
                   if ((float)$matches[2]) {
                      $result = (float)$matches[1] / (float)$matches[2];
                   } else {
                      $error = true;
                   }
                }
                $str = preg_replace($regex, $result, $str, 1);
                $str = str_replace(array('++','--','-+','+-'), array('+','+','-','-'), $str);
         } else {
            $error = true;
         }
      }
    }
}

  if (!$error && (strpos($str, '+') !== false ||  strpos($str, '-') !== false)) {
     $add_sub = true;
     preg_match_all('/([\d\.]+|[\+\-])/', $str, $matches);
     if (isset($matches[0])) {
         $result = 0;
         $operator = '+';
         $tokens = $matches[0];
         $count = count($tokens);
         for ($i=0; $i < $count; $i++) { 
             if ($tokens[$i] == '+' || $tokens[$i] == '-') {
                $operator = $tokens[$i];
             } else {
                $result = ($operator == '+') ? ($result + (float)$tokens[$i]) : ($result - (float)$tokens[$i]);
             }
         }
      }
    }

    if (!$error && !$div_mul && !$add_sub) {
       $result = (float)$str;
    }
    return $error ? 0 : $result;
}

Demo: http://sandbox.onlinephpfunctions.com/code/fdffa9652b748ac8c6887d91f9b10fe62366c650

演示:http: //sandbox.onlinephpfunctions.com/code/fdffa9652b748ac8c6887d91f9b10fe62366c650

回答by mickmackusa

Here is a somewhat verbose bit of code I rolled for another SO question. It does conform to BOMDAS without eval(), but is not equipped to do complex/higher-order/parenthetical expressions. This library-free approach pulls the expression apart and systematically reduces the array of components until all of the operators are removed. It certainly works for your sample expression: 2-1;)

这是我为另一个 SO 问题滚动的有点冗长的代码。它确实符合没有 的BOMDAS eval(),但不具备执行复杂/高阶/括号表达式的能力。这种无库方法将表达式分开并系统地减少组件数组,直到删除所有运算符。它当然适用于您的示例表达式:2-1;)

  1. preg_match()checks that each operator has a numeric substring on each side.
  2. preg_split()divides the string into an array of alternating numbers and operators.
  3. array_search()finds the index of the targeted operator, while it exists in the array.
  4. array_splice()replaces the operator element and the elements on either side of it with a new element that contains the mathematical result of the three elements removed.
  1. preg_match()检查每个运算符的每一侧是否都有一个数字子字符串。
  2. preg_split()将字符串分成交替数字和运算符的数组。
  3. array_search()找到目标运算符的索引,而它存在于数组中。
  4. array_splice()用包含删除的三个元素的数学结果的新元素替换运算符元素及其两侧的元素。

** updated to allow negative numbers **

** 更新为允许负数 **

Code: (Demo)

代码:(演示

$expression="-11+3*1*4/-6-12";
if(!preg_match('~^-?\d*\.?\d+([*/+-]-?\d*\.?\d+)*$~',$expression)){
    echo "invalid expression";
}else{
    $components=preg_split('~(?<=\d)([*/+-])~',$expression,NULL,PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
    var_export($components);  // ['-11','+','3','*','1','*','4','/','-6','-','12']
    while(($index=array_search('*',$components))!==false){
        array_splice($components,$index-1,3,$components[$index-1]*$components[$index+1]);
        var_export($components);
        // ['-11','+','3','*','4','/','-6','-','12']
        // ['-11','+','12','/','-6','-','12']
    }
    while(($index=array_search('/',$components))!==false){
        array_splice($components,$index-1,3,$components[$index-1]/$components[$index+1]);
        var_export($components);  // [-'11','+','-2','-','12']
    }
    while(($index=array_search('+',$components))!==false){
        array_splice($components,$index-1,3,$components[$index-1]+$components[$index+1]);
        var_export($components);  // ['-13','-','12']
    }
    while(($index=array_search('-',$components))!==false){
        array_splice($components,$index-1,3,$components[$index-1]-$components[$index+1]);
        var_export($components); // [-25]
    }
    echo current($components);  // -25
}

Here is a demo of the BOMDAS versionthat uses php's pow()when ^is encountered between two numbers (positive or negative).

这是BOMDAS 版本的演示,它在两个数字(正数或负数)之间遇到pow()时使用 php ^

I don't think I'll ever bother writing a version that handles parenthetical expressions ... but we'll see how bored I get.

我不认为我会费心编写一个处理括号表达式的版本……但我们会看到我有多无聊。