在 JavaScript 中将字符串计算为数学表达式

IT技术 javascript string math numbers
2021-01-17 15:11:07

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

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

6个回答

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

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

或者mathjs,它允许以下内容:

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

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

您可以轻松地执行 + 或 - 操作:

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';
console.log(
  addbits(string)
)

更复杂的数学使 eval 更具吸引力——当然也更容易编写。

我在下面发布了一个更新的答案,其中包含更短的正则表达式并允许运算符之间有空格
2021-03-16 15:11:07
+1 - 可能比我使用的更通用一点,但它不适用于我的情况,因为我可能有 1+-2 之类的东西,而且我希望正则表达式也排除无效语句(我认为你的允许类似于“+3+4+”)
2021-03-24 15:11:07

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

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

@wheresrhys:为什么你认为你的用 JS 编写的解析器会比系统提供的(优化的,可能用 C 或 C++ 编写)更快?
2021-03-20 15:11:07
eval 是迄今为止最快的方法。但是,正则表达式通常不足以确保安全性。
2021-03-30 15:11:07
困扰我的不是安全性(我已经有一个用于工作的正则表达式),更多的是浏览器的负载,因为我必须处理很多这样的字符串。自定义解析器是否可能比 eval() 更快?
2021-04-03 15:11:07
@wheresrhys:为什么你有很多这样的字符串?它们是由程序生成的吗?如果是这样,最简单的方法是在将结果转换为字符串之前计算结果。否则,就是编写自己的解析器的时间了。
2021-04-04 15:11:07

@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;
}

使用它就像

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

更新

这是一个更优化的版本

function addbits(s) {
    return (s.replace(/\s/g, '').match(/[+\-]?([0-9\.]+)/g) || [])
        .reduce(function(sum, value) {
            return parseFloat(sum) + parseFloat(value);
        });
}
这是完美的,只要你只需要加减。这么少的代码,这么多的产品!请放心,它正在被很好地使用:)
2021-04-07 15:11:07

出于同样的目的创建了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

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