Évaluation d'une chaîne en tant qu'expression mathématique en JavaScript

Comment analyser et évaluer une expression mathématique dans une chaîne (par exemple '1+1') sans invoquer eval(string) pour donner sa valeur numérique?

Avec cet exemple, je veux que la fonction accepte '1+1' et retourne 2.

52
demandé sur nbrooks 2010-02-16 23:18:10

14 réponses

Vous pouvez utiliser la bibliothèque Javascript Expression Evaluator , qui vous permet de faire des choses comme:

Parser.evaluate("2 ^ x", { x: 3 });

Ou mathjs, ce qui permet des choses comme:

math.eval('sin(45 deg) ^ 2');

J'ai fini par choisir mathjs pour un de mes projets.

49
répondu Rafael Vega 2013-08-11 14:17:19

Quelqu'un doit analyser cette chaîne. Si ce n'est pas l'interpréteur (via eval), Il faudra que ce soit vous, en écrivant une routine d'analyse pour extraire les nombres, les opérateurs et tout ce que vous voulez soutenir dans une expression mathématique.

Donc, non, il n'y a pas de moyen (simple) sans eval. Si vous êtes préoccupé par la sécurité (parce que l'entrée que vous analysez ne provient pas d'une source que vous contrôlez), vous pouvez peut-être vérifier le format de l'entrée (via un filtre regex de liste blanche) avant de le transmettre à eval?

17
répondu bdukes 2010-02-16 20:38:42

// Vous pouvez faire + ou - facilement:

function addbits(s){
    var total= 0, s= s.match(/[+\-]*(\.\d+|\d+(\.\d+)?)/g) || [];
    while(s.length){
        total+= parseFloat(s.shift());
    }
    return total;
}

var string='1+23+4+5-30';
addbits(string)

Des mathématiques plus compliquées rendent eval plus attrayant-et certainement plus simple à écrire.

17
répondu kennebec 2010-02-16 20:43:02

Je suis allé chercher des bibliothèques JavaScript pour évaluer les expressions mathématiques, et j'ai trouvé ces deux candidats prometteurs:

  • Javascript Expression Evaluator : Plus petit et j'espère plus légèreté. Permet des expressions algébriques, des substitutions et un nombre de fonctions.

  • Mathjs : permet également des nombres complexes, des matrices et des unités. Construit pour être utilisé par JavaScript dans le navigateur et Node.js.

7
répondu Itangalo 2014-02-11 14:16:32

J'ai récemment fait cela en C# (pas D'Eval () pour nous...) en évaluant l'expression en Notation polonaise inverse (c'est le bit facile). La partie la plus difficile est en fait d'analyser cette chaîne et de la transformer en Notation polonaise inverse. J'ai utilisé l'algorithme Shunting Yard car il y a un excellent exemple sur Wikipedia et pseudocode. Je l'ai trouvé vraiment simple à mettre en œuvre les deux et je le recommanderais si vous n'avez pas déjà trouvé de solution ou si vous cherchez des alternatives.

6
répondu RichK 2010-03-13 21:59:26

J'ai créé BigEval dans le même but.
En résolvant des expressions, il fonctionne exactement comme Eval() et prend en charge les opérateurs comme %, ^, &, ** (power) et ! (factoriel). Vous êtes également autorisé à utiliser des fonctions et des constantes (ou disons des variables) dans l'expression. L'expression est résolue dans PEMDAS order qui est commun dans les langages de programmation, y compris JavaScript.

var Obj = new BigEval();
var result = Obj.exec("5! + 6.6e3 * (PI + E)"); // 38795.17158152233
var result2 = Obj.exec("sin(45 * deg)**2 + cos(pi / 4)**2"); // 1
var result3 = Obj.exec("0 & -7 ^ -7 - 0%1 + 6%2"); //-7

Il peut également être fait pour utiliser ces bibliothèques de grands nombres pour l'arithmétique au cas où vous l'êtes traiter les nombres avec une précision arbitraire.

6
répondu Avi 2015-06-30 16:10:51

C'est une petite fonction que j'ai lancée tout à l'heure pour résoudre ce problème - elle construit l'expression en analysant la chaîne un caractère à la fois (c'est en fait assez rapide). Cela prendra n'importe quelle expression mathématique (limitée à +,-,*,/ opérateurs uniquement) et retournera le résultat. Il peut gérer les valeurs négatives et les opérations de nombre illimité ainsi.

Le seul" à faire " est de s'assurer qu'il calcule * & / avant + & -. Ajouter cette fonctionnalité plus tard, mais pour l'instant ce fait ce dont j'ai besoin...

/**
* Evaluate a mathematical expression (as a string) and return the result
* @param {String} expr A mathematical expression
* @returns {Decimal} Result of the mathematical expression
* @example
*    // Returns -81.4600
*    expr("10.04+9.5-1+-100");
*/ 
function expr (expr) {

    var chars = expr.split("");
    var n = [], op = [], index = 0, oplast = true;

    n[index] = "";

    // Parse the expression
    for (var c = 0; c < chars.length; c++) {

        if (isNaN(parseInt(chars[c])) && chars[c] !== "." && !oplast) {
            op[index] = chars[c];
            index++;
            n[index] = "";
            oplast = true;
        } else {
            n[index] += chars[c];
            oplast = false;
        }
    }

    // Calculate the expression
    expr = parseFloat(n[0]);
    for (var o = 0; o < op.length; o++) {
        var num = parseFloat(n[o + 1]);
        switch (op[o]) {
            case "+":
                expr = expr + num;
                break;
            case "-":
                expr = expr - num;
                break;
            case "*":
                expr = expr * num;
                break;
            case "/":
                expr = expr / num;
                break;
        }
    }

    return expr;
}
4
répondu jMichael 2013-05-01 22:02:44

Une alternative à l'excellente réponse de @ kennebec, en utilisant une expression régulière plus courte et en permettant des espaces entre les opérateurs

function addbits(s) {
    var total = 0;
    s = s.replace(/\s/g, '').match(/[+\-]?([0-9\.\s]+)/g) || [];
    while(s.length) total += parseFloat(s.shift());
    return total;
}

Utilisez-le comme

addbits('5 + 30 - 25.1 + 11');

Mise à Jour

Voici une version plus optimisée

function addbits(s) {
    return (s.replace(/\s/g, '').match(/[+\-]?([0-9\.]+)/g) || [])
        .reduce(function(sum, value) {
            return parseFloat(sum) + parseFloat(value);
        });
}
4
répondu Stefan Gabos 2017-08-02 17:57:45

J'ai finalement opté pour cette solution, qui fonctionne pour additionner les entiers positifs et négatifs (et avec une petite modification de l'expression rationnelle fonctionnera aussi pour les décimales):

function sum(string) {
  return (string.match(/^(-?\d+)(\+-?\d+)*$/)) ? string.split('+').stringSum() : NaN;
}   

Array.prototype.stringSum = function() {
    var sum = 0;
    for(var k=0, kl=this.length;k<kl;k++)
    {
        sum += +this[k];
    }
    return sum;
}

Je ne suis pas sûr que ce soit plus rapide que eval (), mais comme je dois effectuer l'opération beaucoup de fois, je suis beaucoup plus à l'aise d'exécuter ce script que de créer des charges d'instances du compilateur javascript

2
répondu wheresrhys 2010-03-06 12:12:39

Essayez nerdamer

var result = nerdamer('12+2+PI').evaluate();
document.getElementById('text').innerHTML = result.text();
<script src="http://nerdamer.com/js/nerdamer.core.js"></script>
<div id="text"></div>
2
répondu ArchHaskeller 2015-09-29 21:57:55

Essayez Le Calculateur Automatique https://github.com/JavscriptLab/autocalculate Calculer la valeur des entrées et la sortie à l'aide des expressions de sélection

Ajoutez simplement un attribut pour votre entrée de sortie comme données-ac="(#firstinput+#secondinput)"

Pas besoin d'initialisation, ajoutez simplement l'attribut data-ac uniquement. Il trouvera automatiquement les éléments ajoutés dynamiquement

Pour ajouter ' Rs ' avec sortie il suffit d'ajouter à l'intérieur du support Bouclé données-ac="{Rs}(#firstinput+#secondinput)"

1
répondu Justin Jose 2017-03-17 10:29:27

Je crois que parseInt et ES6 peut être utile dans cette situation

==> de cette façon:

let func = (str) => {
let arr = str.split("");
return `${Number(arr[0]) + parseInt(arr[1] + Number(arr[2]))}`};
console.log(func("1+1"));

, La chose principale ici est que parseInt analyse le numéro de l'opérateur. Le Code peut être modifié aux besoins correspondants.

1
répondu fuser 2017-06-14 21:46:59

Voici une solution algorithmique similaire à celle de jMichael qui parcourt l'expression caractère par caractère et suit progressivement left / operator / right. La fonction accumule le résultat après chaque tour, elle trouve un caractère opérateur. Cette version ne supporte que les opérateurs ' + ' et ' - ' mais est écrite pour être étendue avec d'autres opérateurs. Note: nous mettons 'currOp' à ' + ' avant de boucler car nous supposons que l'expression commence par un flottant positif. En fait, dans l'ensemble, je fais l'hypothèse cette entrée est similaire à ce qui viendrait d'une calculatrice.

function calculate(exp) {
  const opMap = {
    '+': (a, b) => { return parseFloat(a) + parseFloat(b) },
    '-': (a, b) => { return parseFloat(a) - parseFloat(b) },
  };
  const opList = Object.keys(opMap);

  let acc = 0;
  let next = '';
  let currOp = '+';

  for (let char of exp) {
    if (opList.includes(char)) {
      acc = opMap[currOp](acc, next);
      currOp = char;
      next = '';
    } else {
      next += char;
    } 
  }

  return currOp === '+' ? acc + parseFloat(next) : acc - parseFloat(next);
}
0
répondu internetross 2018-03-02 20:03:34
const getAddition = (str) => {
  return str.split('+').reduce((total, num) => (total + num * 1), 0);
};

const addition = getAddition('1+1');

L'Addition est 2.

0
répondu Rushikesh Bharad 2018-03-30 21:07:33