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

时间:2011-02-20 13:31:52

标签: php math numbers operators eval

我只是想知道PHP是否有一个函数可以使用2-1之类的字符串并生成它的算术结果?

或者我是否必须使用explode()手动执行此操作以获取算术运算符左右的值?

8 个答案:

答案 0 :(得分:52)

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

时,为什么每个答案都会告诉您使用eval,这让我感到困惑。
  

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

在我跳到示例之前,获得我将要使用的课程的地点是PHPClassesGitHubeos.class.phpstack.class.php都是必需的,但可以合并到同一个文件中。

使用这样的类的原因是它包含和中缀到后缀(RPN)解析器,然后是RPN解算器。有了这些,您就不必使用eval功能并打开系统漏洞。一旦有了这些类,就可以使用以下代码来解决一个简单(更复杂)的等式,例如2-1示例。

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

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

因为我真的不希望这只是让我的课程在其中,所以这里有一些其他的选择。自从我使用它8年以来,我只熟悉自己。 ^^

Wolfram|Alpha API
Sage
A fairly bad parser
phpdicecalc

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

无论如何,我想确保在这里用PHP解决方程式的答案并未将所有未来的搜索者都指向eval,因为这是google搜索的顶部。 ^^

答案 1 :(得分:14)

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

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

答案 2 :(得分:8)

这是eval派上用场的案例之一:

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

答案 3 :(得分:7)

您可以使用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

答案 4 :(得分:3)

this论坛中,某人没有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  

答案 5 :(得分:2)

另请参阅此答案:Evaluating a string of simple mathematical expressions

请注意,此解决方案不符合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

答案 6 :(得分:1)

这是我为another SO question推出的一些冗长的代码。它确实符合没有eval() BO MDAS,但没有配备复杂/高阶/括号表达式。这种无库的方法将表达式分开并系统地减少组件数组,直到删除所有运算符。它当然适用于您的示例表达式:2-1;)

  1. preg_match()检查每个运算符的每一侧都有一个数字子字符串。
  2. preg_split()将字符串划分为交替数字和运算符的数组。
  3. array_search()找到目标运算符的索引,但它存在于数组中。
  4. array_splice()用一个新元素替换operator元素及其两侧的元素,该元素包含删除的三个元素的数学结果。
  5. **已更新为允许负数**

    代码:(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
    }
    

    如果在两个数字(正数或负数)之间遇到pow(),则a demo of the BOMDAS version使用php的^

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

答案 7 :(得分:0)

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

更新:我还添加了括号支持。请检查此项目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;
}

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