É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
.
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.
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
?
// 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.
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.
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.
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.
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;
}
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);
});
}
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
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>
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)"
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.
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);
}
const getAddition = (str) => {
return str.split('+').reduce((total, num) => (total + num * 1), 0);
};
const addition = getAddition('1+1');
L'Addition est 2.