【问题标题】:Javascript regex find variables in a math equationJavascript 正则表达式在数学方程中查找变量
【发布时间】:2014-05-14 08:28:36
【问题描述】:

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

示例:

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

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

但它并没有按预期工作。

有什么想法吗?

【问题讨论】:

  • 考虑另一种方式作为第一步(也是独立的) - 将所有“{..}”替换为“”。
  • 使用正则表达式来验证括号对已经很复杂了,为了维护者的理智考虑在正则表达式中执行它的替代方法。
  • 我不想验证表达式,我确定表达式有效。我只想获取未包含在 { 和 } 之间的令牌。
  • 您只有一层 { } 还是可以进行深度嵌套(这会改变一切并需要解析)?
  • 真的改变了一切......

标签: javascript regex


【解决方案1】:

匹配{}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
    }
}

【讨论】:

  • 知道常规语言和上下文无关语言之间区别的 ITT 人员和不知道的人:(
  • 注意:这个答案(和 cmets)是在 OP 没有说最多只有一个级别的 {} 时做出的。最初,这是唯一正确的答案。
【解决方案2】:

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

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

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

【讨论】:

  • 非常有趣的解决方案。我会试试的。
  • 嵌套失败,以及更复杂的情况。
  • 感谢您修复您的答案 :)
  • 我接受这个匹配和过滤表达式的答案,它运行良好且简单。不适用于语法和解析器说明。
【解决方案3】:

你提到的变量名可以通过\b[\w.]+\b匹配,因为它们是由单词分隔符严格限制的

由于您有格式正确的公式,您不想捕获的名称严格遵循},因此您可以使用前瞻表达式来排除这些:

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

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

编辑:

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

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

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

要包含1.2-{abc def} 的匹配,我们需要更改\b1。该术语使用了 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 包含空格,所以它与我们变量名的定义不兼容。

【讨论】:

  • 几乎接近我所需要的。我刚刚添加了处理最后一个示例的空间:匹配“abc def”而不是“abc”和“def”:(\b[\w. ]+\b)(?!})
  • 哦,我的错。改成\w后忘记举报了。很好的收获。
  • 这对于更复杂的表达式仍然很容易失败。例如 - abc {def{}}
  • 如果您想排除 {g{h}i} 输入,您可以使用 (?!(}|{)) 等内容微调前瞻表达式。
  • @BenjaminGruenbaum 该问题已经过编辑,精确到只有一级{}
【解决方案4】:

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

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

既然你知道表达式是有效的,就选择\w+

【讨论】:

  • 我编辑了您的正则表达式以在匹配结果中也允许空格和小数点。单词边界是为了确保我们以“单词”字符开头和结尾。
  • @TimPietzcker,太棒了!谢谢老哥!
  • "abc def+(1.1+{g{h}i})".replace(/{.+?}/g,"").match(/\b[\w. ]+\b/g); 失败
  • "abc def+(1.1+{{}ghi})".replace(/{.+?}/g,"").match(/\b[\w. ]+\b/g); 也失败了顺便说一句,这两个都是完全有效的表达式。
  • 你可以使用循环。测试 /{[^}]+}/ 是否匹配替换为 "" 并重复。如果它与您的完成不匹配。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多