在正则表达式中否定一个特定组

5
如何获取不包含特定组的字符串?
(?:[0-9-+*/()x]|abs|pow|ln|pi|e|a?(sin|cos|tan)h?)+

上面的字符串是一个用于数学表达式的正则表达式。如何获取不是数学表达式的字符串?
示例输入字符串:WIDTH+LENGTH*abs(2)
期望输出:WIDTH LENGTH

只是好奇...既然你使用了非捕获组,那么你用它做什么?语法验证? - Siguza
?: 可以忽略不计。@Siguza - tjvg1991
我脑海中浮现的唯一东西就是负向先行断言 (?!) 或者说是负向后行断言 (?<!)。在使用后行断言时,请确保使用一个固定宽度的捕获组。有用的资源:https://dev59.com/4nRC5IYBdhLWcg3wG9To http://www.regular-expressions.info/lookaround.html - bro
我也检查过了,不行。如果你能给我一个答案就谢谢了,@bro。 - tjvg1991
你能否提供关于你使用的环境的更多细节,例如编程语言、工具等? - bro
我目前正在使用VB6 @bro。 - tjvg1991
3个回答

9
你可以在负向预查中使用正则表达式,然后添加\w字符简写类来匹配包含字母和数字的符号,或者使用带有\b单词边界的[a-zA-Z]
(?![0-9-+*/()x]|abs|pow|ln|pi|e|a?(?:sin|cos|tan)h?)\b[a-zA-Z]+\b

参见正则表达式演示

由于我们只允许使用字母[a-zA-Z],因此我们可以进一步简化为

(?!x|abs|pow|ln|pi|e|a?(?:sin|cos|tan)h?)\b[a-zA-Z]+\b

查看另一个演示

非常感谢!!它起作用了。节省了我很多时间。 - tjvg1991
@Siguza:我同意这不是一个倒置,或许它甚至可以用更好的形式来书写。我猜 [0-9-+*/()x] 不是那么必要的,因为我们只允许 [a-zA-Z]。或许,我们可以将表达式简化为 (?!x|abs|pow|ln|pi|e|a?(?:sin|cos|tan)h?)\b[a-zA-Z]+\b - Wiktor Stribiżew
axabs(3) 上并不像预期那样工作,应该只提取第一个 a - Siguza
@stribizhev 我会说这是 a*abs 的另一种形式,但我只是在和 tjvg 的正则表达式玩耍,试图找到你的正则表达式无法匹配他的正则表达式所匹配的反向情况。 :P - Siguza
1
@tjvg1991:你需要将[a-zA-Z]类扩展到[a-zA-Z0-9] => (?!x|abs|pow|ln|pi|e|a?(?:sin|cos|tan)h?)\b[a-zA-Z0-9]+\b - Wiktor Stribiżew
显示剩余3条评论

3

当你想要在正则表达式中“跳过”某些表达式时,可以使用以下方法:

"Tarzan"|skip1|skip2|skip3|more|complicated|expressions|here|(Tarzan)

当你迭代正则表达式匹配集合时,只需要那些在第一个捕获组中有任何内容的匹配项,忽略其他匹配项即可。这就像The Best Regex Trick Ever一样简单。

不需要使用复杂的环视,因为通常无法处理重叠边缘情况。


1

虽然stribizhev's answer在大多数情况下可能有效,但它不是与问题中的正则表达式完全相反的,因为两个正则表达式都无法匹配以下内容:

  • 空格
  • 特殊字符,例如?!^~;:_,.[]{}<>(可能还有更多)

而两个正则表达式匹配以下内容:

  • 字符串,例如axabs(3),其中xabs部分被两者匹配。

这可能可以通过微调来解决,但是我想要一个真正的相反! :P

所以在这里:

(?:(?!e|ln|(?<=l)n|pi|(?<=p)i|abs|(?<=a)bs|(?<=ab)s|pow|(?<=p)ow|(?<=po)w|sin|(?<=s)in|(?<=si)n|cos|(?<=c)os|(?<=co)s|tan|(?<=t)an|(?<=ta)n|asin|acos|atan)[^0-9-+*/()x])+

它的工作原理如下:

  1. 匹配任何一个不是 0-9-+*/()x 中的字符(即 [^0-9-+*/()x])。
  2. 但如果该字符匹配了某个特定的前/后缀模式,并且本身是某个特定字符,则不匹配该字符。
    使用负向先行断言((?!...))意味着每个 | 后面的第一个字符是当前字符,其后的字符是跟随当前字符的字符,而 (?<=) 是一个负向后行断言,匹配某些前缀字符。
    例如,为了不匹配 sin,我们需要“不匹配” s,如果后面跟着 in,不匹配 i,如果前面是 s,后面是 n,并且不匹配 n,如果前面是 si
    在正则表达式中(仅限于环视部分):(?!sin|(?<=s)in|(?<=si)n)
    对于 elnpi 等构建完整列表的结果如下:

    (?!e|ln|(?<=l)n|pi|(?<=p)i|abs|(?<=a)bs|(?<=ab)s|pow|(?<=p)ow|(?<=po)w|sin|(?<=s)in|(?<=si)n|cos|(?<=c)os|(?<=co)s|tan|(?<=t)an|(?<=ta)n|asin|acos|atan)
    
  3. 匹配上述内容一次或多次((?:...)+)。

通过将像(?<=l)n(?<=si)n(?<=ta)n这样的部分合并为(?<=l|si|ta)n,可以缩短正则表达式的长度:

(?:(?!e|ln|(?<=l|si|ta)n|pi|(?<=p)i|abs|(?<=a)bs|(?<=ab|co)s|pow|(?<=p)ow|(?<=po)w|a?(?:sin|cos|tan)|(?<=s)in|(?<=c)os|(?<=t)an)[^0-9-+*/()x])+

您可以在Debuggex上查看此演示以及美观的可视化。

注意1:此正则表达式在JavaScript中无法工作,因为JS-regex不支持向后查找。
注意2:在Debuggex中将单个多字节字符(例如§°☀☁️❄️)附加到测试字符串可能会导致其似乎出现故障,但这不是我的正则表达式的问题,可以通过例如PHP进行验证。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接