Cómo evaluar matemáticamente una cadena como "2-1" para producir "1"?


Me preguntaba si PHP tiene una función que pueda tomar una cadena como 2-1 y producir el resultado aritmético de la misma.

O tendré que hacer esto manualmente con explode() para obtener los valores a la izquierda y a la derecha del operador aritmético?

Author: mickmackusa, 2011-02-20

7 answers

Sé que esta pregunta es antigua, pero me encontré con ella anoche mientras buscaba algo que no estaba del todo relacionado, y cada respuesta aquí es mala. No solo malo, muy malo. Los ejemplos que doy aquí serán de una clase que creé en 2005 y pasé las últimas horas actualizando para PHP5 debido a esta pregunta. Existen otros sistemas, y existían antes de que se publicara esta pregunta, por lo que me desconcierta por qué cada respuesta aquí te dice que uses eval, cuando la advertencia de PHP es:

La construcción del lenguaje eval() es muy peligrosa porque permite la ejecución de código PHP arbitrario. Por lo tanto, se desaconseja su uso. Si ha verificado cuidadosamente que no hay otra opción que usar esta construcción, preste especial atención a no pasar ningún dato proporcionado por el usuario sin validarlo adecuadamente de antemano.

Antes de saltar al ejemplo, los lugares para obtener la clase que usaré están en cualquiera de los dos PHPClassesor GitHub. Ambos eos.class.php y stack.class.php son necesarios, pero se pueden combinar en el mismo archivo.

La razón para usar una clase como esta es que incluye un analizador de infix a postfix(RPN), y luego un solucionador de RPN. Con estos, nunca tendrá que usar la función eval y abrir su sistema a vulnerabilidades. Una vez que tenga las clases, el siguiente código es todo lo que se necesita para resolver una ecuación simple (a más compleja) como su 2-1 ejemplo.

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

¡Eso es todo! Puedes usar un analizador como este para la mayoría de las ecuaciones, aunque sean complicadas y anidadas sin tener que recurrir al ' mal eval'.

Porque realmente no quiero que esto solo tenga mi clase en él, aquí hay algunas otras opciones. Solo estoy familiarizado con el mío ya que lo he estado usando durante 8 años. ^^

Wolfram|Alpha API
Sage
Una bastante mala analizador de
phpdicecalc

No estoy muy seguro de lo que pasó con otros que había encontrado anteriormente - me encontré con otro en GitHub antes también, desafortunadamente no lo marcé, pero estaba relacionado con grandes operaciones flotantes que incluían un analizador también.

De todos modos, quería asegurarme de que una respuesta para resolver ecuaciones en PHP aquí no apuntara a todos los buscadores futuros a eval ya que esto estaba en la parte superior de una búsqueda de Google. ^^

 50
Author: Jon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-01 03:45:11
$operation='2-1';
eval("\$value = \"$operation\";");

O

$value=eval("return ($op);");
 12
Author: dynamic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-20 13:43:39

Este es uno de los casos en los que eval es útil:

$expression = '2 - 1';
eval( '$result = (' . $expression . ');' );
echo $result;
 8
Author: gion_13,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-30 20:18:19

Puedes usar BC Math arbitrary precision

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

 6
Author: delphist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-20 13:53:01

En este foro alguien lo hizo sin eval. ¿Tal vez puedas intentarlo? Créditos para ellos, acabo de encontrarlo.

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
Author: AmirG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-03 06:03:00

También vea esta respuesta aquí: Evaluando una cadena de expresiones matemáticas simples

Tenga en cuenta que esta solución NO se ajusta a BODMAS, pero puede usar corchetes en su cadena de evaluación para superar esto.

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
Author: kurdtpage,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:17

Aquí hay un poco detallado de código que rodé para otra pregunta SO. Se ajusta a BO MDAS sin eval(), pero no está equipado para hacer expresiones complejas/de orden superior/entre paréntesis. Este enfoque libre de bibliotecas separa la expresión y reduce sistemáticamente la matriz de componentes hasta que se eliminan todos los operadores. Ciertamente funciona para su expresión de muestra: 2-1 ;)

  1. preg_match() comprueba que cada operador tiene un valor numérico subcadena a cada lado.
  2. preg_split() divide la cadena en una matriz de números y operadores alternos.
  3. array_search() encuentra el índice del operador de destino, mientras que existe en la matriz.
  4. array_splice() reemplaza el elemento operador y los elementos a cada lado de él con un nuevo elemento que contiene el resultado matemático de los tres elementos eliminados.

* * actualizado para permitir números negativos * *

Código: ( 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
}

Aquí es una demostración de la B versión OMDAS que usa php pow() cuando ^ se encuentra entre dos números (positivo o negativo).

Creo que nunca me molestaré en escribir una versión que maneje expresiones entre paréntesis ... pero veremos cómo me aburro.

 1
Author: mickmackusa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-19 13:16:00