如何在regex中否定特定单词?

我知道我可以像[^bar]中那样否定一组字符,但我需要一个正则表达式,其中否定适用于特定的单词-所以在我的示例中,我如何否定实际的bar,而不是“bar中的任何字符”?

981161 次浏览

您可以使用消极的向前看或向后看

^(?!.*?bar).*^(.(?<!bar))*?$

或者只使用基础知识:

^(?:[^b]+|b(?:$|[^a]|a(?:$|[^r])))*$

这些都匹配不包含bar的任何内容。

除非性能是最重要的,否则通常更容易通过第二次运行结果,跳过那些与您想要否定的单词匹配的结果。

正则表达式通常意味着你正在做脚本或某种性能低下的任务,所以找到一个易于阅读、易于理解和易于维护的解决方案。

下面的正则表达式将做你想要的(只要支持负lookbehinds和lookahead),正确匹配事物;唯一的问题是它匹配单个字符(即每个匹配是单个字符,而不是两个连续的“bar”之间的所有字符),如果您使用非常长的字符串,可能会导致高开销。

b(?!ar)|(?<!b)a|a(?!r)|(?<!ba)r|[^bar]

一个很好的方法是使用消极前瞻

^(?!.*bar).*$

负前瞻结构是一对括号,开括号后跟问号和感叹号。在前瞻内部[是任何正则表达式模式]。

只是想了一些可以做的事情。它与我的第一个答案非常不同,因为它不使用正则表达式,所以我决定制作第二个答案帖子。

在字符串上使用您选择的语言的split()等效方法,并将单词否定作为拆分的参数。使用Python的示例:

>>> text = 'barbarasdbarbar 1234egb ar bar32 sdfbaraadf'>>> text.split('bar')['', '', 'asd', '', ' 1234egb ar ', '32 sdf', 'aadf']

这样做的好处是,至少在Python中(我不记得在Visual Basic或Java中的功能是否相同),它可以让您间接知道“bar”在字符串中重复的时间,因为“bar”之间的空字符串包含在结果列表中(尽管开头的空字符串是因为字符串开头有一个“bar”)。如果你不想这样,你可以简单地从列表中删除空字符串。

我在试图为以下英文语句确定正则表达式时遇到了这个论坛线程:

给定一个输入字符串,匹配一切除非这个输入字符串正好是'bar';例如,我想匹配'障碍'和'disbar'以及'foo'。

这是我想出来的正则表达式

^(bar.+|(?!bar).*)$

我的正则表达式的英文翻译是“匹配字符串,如果它以'bar'开头并且它至少有一个其他字符,或者如果字符串不以'bar'开头。

我有一个文件名列表,我想通过这种行为排除某些文件名(Ruby):

files = ['mydir/states.rb',      # don't match these'countries.rb','mydir/states_bkp.rb',  # match these'mydir/city_states.rb']excluded = ['states', 'countries']
# set my_rgx here
result = WankyAPI.filter(files, my_rgx)  # I didn't write WankyAPI...assert result == ['mydir/city_states.rb', 'mydir/states_bkp.rb']

以下是我的解决方案:

excluded_rgx = excluded.map{|e| e+'\.'}.join('|')my_rgx = /(^|\/)((?!#{excluded_rgx})[^\.\/]*)\.rb$/

我对这个应用程序的假设:

  • 要排除的字符串位于输入的开头,或紧跟斜杠。
  • 允许的字符串以.rb结尾。
  • 允许的文件名在.rb之前没有.字符。

接受的答案很好,但实际上是正则表达式中缺乏简单子表达式否定运算符的解决方法。这就是grep --invert-match退出的原因。所以在*nix中,你可以使用管道和第二个正则表达式来完成所需的结果。

grep 'something I want' | grep --invert-match 'but not these ones'

仍然是一个变通办法,但也许更容易记住。

解决方案:

^(?!.*STRING1|.*STRING2|.*STRING3).*$

xxxxxx好的

xxxSTRING1xxxKO(是否需要)

xxxSTRING2xxxKO(是否需要)

xxxSTRING3xxxKO(是否需要)

我希望补充已接受的答复,并以我迟到的答复为讨论做出贡献。

@ChrisVanOpror分享了这个regex教程,这是学习正则表达式的绝佳资源。

然而,通读真的很耗时。

为了方便记忆,我做了一张备忘单。

这个参考是基于大括号[](){}领先每个类,我发现很容易回忆。

Regex = {'single_character': ['[]', '.', {'negate':'^'}],'capturing_group' : ['()', '|', '\\', 'backreferences and named group'],'repetition'      : ['{}', '*', '+', '?', 'greedy v.s. lazy'],'anchor'          : ['^', '\b', '$'],'non_printable'   : ['\n', '\t', '\r', '\f', '\v'],'shorthand'       : ['\d', '\w', '\s'],}

这一评论中提取bkDJ

^(?!bar$).*

这个解决方案的优点是可以清楚地否定(排除)多个单词:

^(?!bar$|foo$|banana$).*

如果它确实是你不想匹配的bar,那么:

^(?!.*\bbar\b).*$

以上将匹配任何不包含bar的字边界上的字符串,也就是说,与非字字符分开。但是,上述模式中使用的句点/点(.)将不匹配换行符,除非使用正确的正则表达式标志:

^(?s)(?!.*\bbar\b).*$

或者:

^(?!.*\bbar\b)[\s\S]*$

我们不使用任何特殊的标志,而是寻找任何空白或非空白的字符。这应该涵盖每个字符。

但是,如果我们想匹配可能包含bar的单词,但不是特定的单词bar呢?

(?!\bbar\b)\b\[A-Za-z-]*bar[a-z-]*\b
  1. (?!\bbar\b)断言下一个输入不是字边界上的bar
  2. \b\[A-Za-z-]*bar[a-z-]*\b匹配包含bar的单词边界上的任何单词。

见正则表达式演示