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

时间:2014-05-14 08:28:37

标签: javascript regex

我想在数学表达式中找到未包含在{}

之间的元素

示例:

  • 输入: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:

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

但它没有按预期工作。

有什么想法吗?

4 个答案:

答案 0 :(得分:3)

对于标准正则表达式匹配{},特别是嵌套的很难(读不可能),因为它需要计算您遇到的{的数量,因此您知道哪个}终止了它。

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

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

答案 1 :(得分:1)

这可能是一个有趣的正则表达式挑战,但在现实世界中,只需查找所有[^+/*()-]+组并删除{}

"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",
            "}"
         ]
      ],
      ")"
   ]
]

然后你可以正常迭代这个数组并获取你感兴趣的部分。

答案 2 :(得分:1)

您提到的变量名称可以与\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不包含空格,因此它与变量名的定义不兼容。

答案 3 :(得分:0)

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

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

由于您知道表达式有效,只需选择\w+

即可