Javascript 正则表达式在数学方程中查找变量

Javascript regex find variables in a math equation

本文关键字:方程中 查找 变量 正则表达式 Javascript      更新时间:2023-09-26

我想在数学表达式中找到未在{}之间包装的元素

例子:

  • 输入:abc+1*def
    比赛: ["abc", "1", "def"]

  • 输入:{abc}+1+def
    比赛: ["1", "def"]

  • 输入:abc+(1+def)
    比赛: ["abc", "1", "def"]

  • 输入:abc+(1+{def})
    比赛: ["abc", "1"]

  • 输入:abc def+(1.1+{ghi})
    比赛: ["abc def", "1.1"]

  • 输入:1.1-{abc def}
    比赛: ["1.1"]

规则

  • 表达式格式正确。(所以不会有没有右括号的开始括号或没有}的开始{(
  • 表达式中允许的数学符号是+ - / *( )
  • 数字可以是小数。
  • 变量可以包含空格。
  • 只有一个级别的{ }(无嵌套括号(

到目前为止,我以:http://regex101.com/r/gU0dO4

(^[^/*+({})-]+|(?:[/*+({})-])[^/*+({})-]+(?:[/*+({})-])|[^/*+({})-]+$)

我将任务分为 3 个:

  • 匹配字符串开头的元素
  • 匹配两个 { 和 } 之间的元素
  • 匹配字符串末尾的元素

但它没有按预期工作。

知道吗?

对于标准正则表达式来说,匹配 {} s,尤其是嵌套的 s,是很困难的(不可能读取(,因为它需要计算您遇到的{的数量,以便您知道哪个}终止了它。

相反,一个简单的字符串操作方法可以工作,这是一个非常基本的解析器,它只是从左到右读取字符串并在括号之外使用它。

var input = "abc def+(1.1+{ghi})"; // I assume well formed, as well as no precedence
var inParens = false;
var output = [], buffer = "", parenCount = 0;
for(var i = 0; i < input.length; i++){
    if(!inParens){
          if(input[i] === "{"){
              inParens = true;
              parenCount++;
          } else if (["+","-","(",")","/","*"].some(function(x){ 
               return x === input[i]; 
          })){ // got symbol
              if(buffer!==""){ // buffer has stuff to add to input
                  output.push(buffer); // add the last symbol
                  buffer = "";
              }
          } else { // letter or number
              buffer += input[i]; // push to buffer
          }
    } else { // inParens is true
         if(input[i] === "{") parenCount++;
         if(input[i] === "}") parenCount--;
         if(parenCount === 0) inParens = false; // consume again
    }
}

这可能是一个有趣的正则表达式挑战,但在现实世界中,您最好简单地找到所有[^+/*()-]+组并删除包含在{} 中的组。

"abc def+(1.1+{ghi})".match(/[^+/*()-]+/g).filter(
    function(x) { return !/^{.+?}$/.test(x) })
// ["abc def", "1.1"]

话虽如此,正则表达式不是解析数学表达式的正确方法。对于严肃的分析,请考虑使用正式语法和解析器。有很多用于javascript的解析器生成器,例如,在PEG中.js您可以编写类似的语法

expr
  = left:multiplicative "+" expr
  / multiplicative
multiplicative
  = left:primary "*" right:multiplicative
  / primary
primary
  = atom
  / "{" expr "}"
  / "(" expr ")"
atom = number / word
number = n:[0-9.]+ { return parseFloat(n.join("")) }
word = w:[a-zA-Z ]+ { return w.join("") }

并生成一个能够转动的解析器

 abc def+(1.1+{ghi})

[
   "abc def",
   "+",
   [
      "(",
      [
         1.1,
         "+",
         [
            "{",
            "ghi",
            "}"
         ]
      ],
      ")"
   ]
]

然后,您可以正常迭代此数组并获取您感兴趣的部分。

您提到的变量名称可以按'b['w.]+'b匹配,因为它们受到单词分隔符的严格限制

由于您具有格式良好的公式,因此您不想捕获的名称后面严格跟着 } ,因此您可以使用前瞻表达式来排除这些:

('b['w.]+ 'b)(?!})

将匹配所需的元素 (http://regexr.com/38rch(。

编辑:

对于更复杂的用途,例如正确匹配:

  • ABC {def{}}
  • abc def+(1.1+{g{h}i}(

我们需要将前瞻术语更改为(?|({|}))

要包含1.2-{abc def}匹配项,我们需要更改'b 1。这个术语使用在javascript中不可用的环顾表达式。所以我们必须解决。

(?:^|[^a-zA-Z0-9. ])([a-zA-Z0-9. ]+(?=[^0-9A-Za-z. ]))(?!({|}))

对于我们的例子来说似乎是一个很好的例子(http://regex101.com/r/oH7dO1(。

1 'b'w'W 'z'a之间的分离。由于'w不包含空格,而'W包含空格,因此它与我们的变量名称的定义不兼容。

继续使用 user2864740 的评论,您可以将 {} 之间的所有内容替换为空,然后匹配其余内容。

var matches = "string here".replace(/{.+?}/g,"").match(/'b['w. ]+'b/g);

由于您知道表达式是有效的,因此只需选择'w+