web-dev-qa-db-ja.com

「2-1」のような文字列を数学的に評価して「1」を生成する方法は?

PHPに_2-1_のような文字列を取り、その算術結果を生成する関数があるかどうか疑問に思っていましたか?

または、算術演算子の左右の値を取得するためにexplode()を使用して手動でこれを行う必要がありますか?

27
Nikola

私はこの質問が古いことを知っていますが、あまり関係のないものを探しているときに昨夜それを見つけました、そしてここでのすべての答えは悪いです。悪いだけでなく、非常に悪い。ここで提供する例は、2005年に作成したクラスからのものであり、この質問のために過去数時間PHP5の更新に費やしました。他のシステムが存在し、この質問が投稿される前に存在していたため、ここでのすべての回答で eval を使用するように指示されている理由がわかりませんPHPは:

Eval()言語構造は、任意のPHPコードの実行を許可するため、非常に危険です。そのため、その使用はお勧めしません。この構造を使用する以外にオプションがないことを注意深く確認した場合、ユーザーが提供したデータを事前に適切に検証せずに渡さないように特に注意してください。

例にジャンプする前に、使用するクラスを取得する場所は PHPClasses または GitHub のいずれかにあります。 eos.class.phpstack.class.phpの両方が必要ですが、同じファイルに組み合わせることができます。

このようなクラスを使用する理由は、postfix(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
セージ
かなり悪いパーサー
phpdicecalc

以前見つけた他の人に何が起こったのかよくわかりません-以前にGitHubで別の人に遭遇しましたが、残念ながらブックマークしませんでしたが、これはパーサーを含む大きなfloat操作にも関連していました。

とにかく、ここでPHP=)の方程式を解くための答えが、すべての将来の検索者をevalに向けていないことを確認したかったのです。 ^^

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

または

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

これは eval が便利なケースの1つです。

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

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

7
delphist

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  
3
AmirG

こちらの回答もご覧ください。 単純な数式の文字列の評価

このソリューションは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
2
kurdtpage

これは私がロールしたコードのやや冗長なビットです another SO question 。これは BOeval()なしのMDASですが、複雑な/高次の/括弧で囲まれた式を実行する機能がありません。このライブラリを使用しないアプローチでは、式がばらばらになり、すべての演算子が削除されるまで、コンポーネントの配列が体系的に削減されます。これは確かにサンプル式で機能します:_2-1_;)

  1. preg_match()は、各演算子の両側に数値サブストリングがあることを確認します。
  2. preg_split()は、文字列を交互の数値と演算子の配列に分割します。
  3. array_search()は、対象の演算子のインデックスを検索しますが、配列には存在します。
  4. array_splice()は、演算子要素とその両側の要素を、削除された3つの要素の数学的結果を含む新しい要素に置き換えます。

**負の数を許可するように更新**

コード:( デモ

_$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
}
_

これが のデモです BOMDASバージョン 2つの数値(正または負)の間で_^_が検出された場合、phpのpow()を使用します。

かっこ式を処理するバージョンを作成する気になることはないと思います...しかし、私がどれほど退屈になるかはわかります。

1
mickmackusa

Create_functionが非推奨になり、文字列を数学として評価する代替の軽量ソリューションが完全に必要になったためです。数時間かけて、次のことを思いつきました。ちなみに私の場合は必要ないのでカッコは気にしませんでした。演算子の優先順位を正しく適合させるものが必要だった。

更新:括弧のサポートも追加しました。このプロジェクトをチェックしてください Emathate 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/fdffa9652b748ac8c6887d91f9b10fe62366c65

1
Samir