我想在数学表达式中找到未在{
和}
之间包装的元素
例子:
-
输入:
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+