【问题标题】:How to negate specific word in regex? [duplicate]如何否定正则表达式中的特定单词? [复制]
【发布时间】:2010-11-17 10:30:10
【问题描述】:

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

【问题讨论】:

标签: regex


【解决方案1】:

您可以使用negative look-ahead or look-behind:

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

或者只使用基础知识:

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

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

【讨论】:

  • 哪些语言不支持正则表达式中的(否定)后视和/或(否定)前瞻?
  • 我认为重点是,看看你的模式,你所做的只是拒绝“bar”这个词。
  • @Bryan:事实上,它并没有拒绝“bar”这个词。它只是在“ar”之后拒绝“b”。
  • 好主意,但并非所有地方都支持。 Afaik Javascript 支持负前瞻,但不支持后视。我不知道其他语言的详细信息,但这可能会有所帮助:en.wikipedia.org/wiki/Comparison_of_regular_expression_engines
  • @JAB bash 不支持消极的后视/前瞻。
【解决方案2】:

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

正则表达式通常意味着您正在执行脚本或某种低性能任务,因此请找到一个易于阅读、易于理解且易于维护的解决方案。

【讨论】:

  • 在很多情况下您无法控制工作流程:您只需编写一个作为过滤器的正则表达式。
  • 如果你想替换所有与某个正则表达式不匹配的文本?
  • 这个想法很特别,但确实有效。大多数答案是针对PCRE,但它不能将他们的解决方案应用于re2
【解决方案3】:

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

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

【讨论】:

  • 相比那些迫使我们在得到最终答案之前阅读错误答案的多次更新,为什么不重写你的答案以使其完整,但没有一些令人困惑的坏部分?如果有人真的关心编辑历史,他们可以使用本网站的内置功能。
  • 我写这个答案已经两年半了,但可以肯定。
  • 该死的疼,试试这个 (?:(?!bar).)*
  • @Mary,这不会按预期工作。例如/(?:(?!bar).)*/g on foobar 返回foo AND ar
【解决方案4】:

一个很好的方法是使用negative lookahead

^(?!.*bar).*$

否定的前瞻结构是一对括号,左括号后跟一个问号和一个感叹号。在前瞻内[是任何正则表达式模式]。

【讨论】:

  • 这说明了一切(我可能会从 (?!bar) 开始并建立起来)。我不明白为什么其他人把它弄得这么复杂。
  • 行首字符在开头做得很好。
  • 做得很好 - 匹配具有指定字符串的行,并且该字符串前面没有任何内容,并且该字符串后面跟着任何内容。根据定义,这是没有字符串!因为如果存在,即使它是一个线锚^,它也会总是在某些东西之前
  • @NeilTraft grep -v bar 怎么样 :)
  • 如果您使用 grep,请使用 -P 选项。 -P 启用 perl 正则表达式。例如grep -P '(?!不包含此字符串)'
【解决方案5】:

只是想到了可以做的其他事情。和我的第一个答案很不一样,因为它不使用正则表达式,所以我决定做第二个答案。

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

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

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

【讨论】:

  • @Ajk_P 是的,但是这种答案可能会帮助 OP 跳出框框思考,他们可能一直专注于正则表达式,却没有意识到没有它们也可以解决。
【解决方案6】:

我在尝试为以下英文语句识别正则表达式时遇到了这个论坛主题:

给定一个输入字符串,匹配所有内容 除非这个输入字符串正好是'bar';例如,我想匹配 'barrier' 和 'disbar' 以及 'foo'。

这是我想出的正则表达式

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

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

【讨论】:

  • @ReReqest - 如果您将这个问题作为一个单独的问题发布,您将有更好的机会回答这个问题。如果你愿意,你可以提供这个问题的链接。对于问题的实质 - 它看起来不错,但我不是正则表达式大师
  • 那是我要找的那个。它确实匹配除 bar 之外的所有内容。
  • ^(?!bar$).* 与此匹配(除 bar 之外的所有内容)并避免重复。
【解决方案7】:

我有一个文件名列表,我想通过这种行为排除某些文件名(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 之前没有. 字符。

【讨论】:

    【解决方案8】:

    接受的答案很好,但对于正则表达式中缺少简单的子表达式否定运算符确实是一种解决方法。这就是grep --invert-match 退出的原因。因此,在 *nixes 中,您可以使用管道和第二个正则表达式来实现所需的结果。

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

    仍然是一种解决方法,但可能更容易记住。

    【讨论】:

    • 这对于使用 grep 的人来说是正确的答案,它当然符合正则表达式的条件。我只是希望这个答案更突出(甚至包含在接受的答案中),这样我就没有先花时间处理其他答案。
    • 我在 R 中看不到 invert match 选项。它仅限于 unix grep 吗?
    【解决方案9】:

    解决方案:

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

    xxxxxx 好的

    xxxSTRING1xxx KO(是否要)

    xxxSTRING2xxx KO(是否要)

    xxxSTRING3xxx KO(是否要)

    【讨论】:

    • 谢谢,这为我提供了多个单词所需的额外信息
    • 我是唯一一个讨厌“OK”和“KO”作为通过测试指标的人吗?离灾难只有一个错别字...
    【解决方案10】:

    我希望用我迟到的答案来补充已接受的答案并为讨论做出贡献。

    @ChrisVanOpstal 分享了@98​​7654321@,这是学习正则表达式的绝佳资源。

    但是,通读一遍真的很费时间。

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

    这个参考是基于大括号[](){}领导每个班级,我觉得很容易回忆。

    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'],
     }
    

    【讨论】:

      【解决方案11】:

      bkDJthis comment中提取:

      ^(?!bar$).*
      

      此解决方案的优点是可以明确否定(排除)多个单词:

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

      【讨论】:

      • 为什么需要尾随.*
      【解决方案12】:

      如果它真的是一个你不想匹配的单词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 的单词边界上的任何单词。

      See Regex Demo

      【讨论】:

        猜你喜欢
        • 2020-04-20
        • 1970-01-01
        • 1970-01-01
        • 2014-07-06
        • 2021-04-20
        • 2019-09-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多