在JavaScript中将字符串评估为数学表达式

whe*_*hys 62 javascript string math numbers

如何解析和评估字符串中的数学表达式(例如'1+1')而不调用eval(string)它的数值?

在这个例子中,我希望函数接受'1+1'并返回2.

Raf*_*ega 57

您可以使用JavaScript Expression Evaluator库,它允许您执行以下操作:

Parser.evaluate("2 ^ x", { x: 3 });
Run Code Online (Sandbox Code Playgroud)

或者mathjs,允许这样的东西:

math.eval('sin(45 deg) ^ 2');
Run Code Online (Sandbox Code Playgroud)

我最终为我的一个项目选择了mathjs.


小智 21

//你可以轻松地做+或 -

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)
Run Code Online (Sandbox Code Playgroud)

更复杂的数学使eval更具吸引力 - 当然也更简单.

  • +1 - 可能比我使用的更通用,但它不适用于我的情况,因为我可能有类似1 + -2的东西,我希望正则表达式也排除无效的语句(我认为你的允许像"+ 3 + 4 +"这样的东西 (2认同)

bdu*_*kes 17

有人必须解析那个字符串.如果它不是解释器(via eval)那么它将需要你,编写一个解析例程来提取数字,运算符以及你想要在数学表达式中支持的任何其他东西.

所以,不,没有任何(简单)方式eval.如果您担心安全性(因为您正在解析的输入不是来自您控制的源),也许您可​​以在将输入传递给之前检查输入的格式(通过白名单正则表达式过滤器)eval

  • @wheresrhys:为什么你认为用JS编写的解析器会比系统提供的解析器更快(优化,可能用C或C++编写)? (11认同)
  • eval是迄今为止最快的方法.但是,正则表达式通常不足以确保安全性. (4认同)

Avi*_*Avi 9

我为同样的目的创建了BigEval.
在求解表达式时,它执行与Eval()%,^,&,**(幂)和!等运算符完全相同并支持它们.(阶乘).您还可以在表达式中使用函数和常量(或说变量).表达式以PEMDAS顺序求解,这在包括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
Run Code Online (Sandbox Code Playgroud)

如果您处理具有任意精度的数字,也可以使用那些Big Number库进行算术运算.


小智 9

简单而优雅的搭配 Function()

function parse(str) {
  return Function(`'use strict'; return (${str})`)()
}

parse("1+2+3"); 
Run Code Online (Sandbox Code Playgroud)

  • 我不明白这比 eval 有什么更好的地方。在运行此服务器端之前,请注意“parse('process.exit()')”。 (10认同)
  • 请小心,这可能会带来严重的安全风险,具体取决于用户输入的来源。这会解释任何 JavaScript 代码,例如 `parse('alert("hello")')`。 (2认同)
  • 我明白这一点,你也明白这一点。然而,从 Google 找到该网站的普通用户很可能不会理解这一点,这就是为什么将任何具有重大安全隐患的建议都标记为如此重要的原因。 (2认同)

Ste*_*bos 8

@kennebec的出色答案的替代方案,使用更短的正则表达式并在运算符之间留空格

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;
}
Run Code Online (Sandbox Code Playgroud)

像这样使用

addbits('5 + 30 - 25.1 + 11');
Run Code Online (Sandbox Code Playgroud)

更新资料

这是更优化的版本

function addbits(s) {
    return (s.replace(/\s/g, '').match(/[+\-]?([0-9\.]+)/g) || [])
        .reduce(function(sum, value) {
            return parseFloat(sum) + parseFloat(value);
        });
}
Run Code Online (Sandbox Code Playgroud)


Ita*_*alo 7

我去寻找用于评估数学表达式的JavaScript库,并找到了这两个有希望的候选者:

  • JavaScript Expression Evaluator:更小,希望更轻量级.允许代数表达式,替换和许多函数.

  • mathjs:允许复数,矩阵和单位.内置以供浏览器中的JavaScript和Node.js使用.


Ric*_*chK 6

我最近在C#中做了这个(我们没有Eval()...)通过评估反向波兰表示法中的表达式(这很容易).困难的部分实际上是解析字符串并将其转换为反向波兰表示法.我使用了Shunting Yard算法,因为在维基百科和伪代码上有一个很好的例子.我发现实现它们非常简单,我建议如果你还没有找到解决方案或正在寻找替代方案.


小智 6

这是我刚刚拼凑起来的一个小函数来解决这个问题 - 它通过一次分析一个字符的字符串来构建表达式(虽然它实际上非常快)。这将采用任何数学表达式(仅限于 +、-、*、/ 运算符)并返回结果。它也可以处理负值和无限数量的操作。

剩下的唯一“要做”就是确保它在 + & - 之前计算 * & /。稍后会添加该功能,但现在这满足了我的需求......

/**
* 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;
}
Run Code Online (Sandbox Code Playgroud)


whe*_*hys 3

我最终选择了这个解决方案,它适用于对正整数和负整数求和(并且对正则表达式进行一些修改也适用于小数):

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;
}
Run Code Online (Sandbox Code Playgroud)

我不确定它是否比 eval() 更快,但由于我必须多次执行该操作,所以我运行这个脚本比创建 javascript 编译器的实例负载要舒服得多