如何使用正则表达式进行“反向匹配”?

162

我正在逐行处理一个文件,并且想要进行反向匹配。例如,我希望匹配包含六个字母字符串的行,但仅当这些六个字母不是“Andrea”时才匹配。我该如何做?

我正在使用RegexBuddy,但仍然遇到困难。


1
实际上,你最好提供更多关于你正在做的事情的信息,看看是否有人可以提供替代方案。通常,通过构建匹配每行的正则表达式来尝试解析整个文件是一条相当复杂的路线 :) - Dan
10个回答

99
(?!Andrea).{6}

假设您的正则表达式引擎支持负向先行断言...

...或者您更喜欢使用[A-Za-z]{6}代替.{6}

请注意,先行和后行断言通常不是“反转”正则表达式匹配的正确方式。正则表达式并没有真正设置用于执行负向匹配;它们将这一任务留给您使用它们的语言来处理。


1
你需要添加@Vinko Vrsalovic使用的^,这样它就不会匹配“ndrea\n”了。 - bdukes
2
默认情况下,点号“.”在匹配时不包括换行符“\n”(一些语言[例如Perl]允许您切换到该行为,但默认情况下,“.”匹配除了“\n”之外的所有内容)。 - Dan
1
(另外,OP从未提到字符串必须出现在行的开头) - Dan
1
Andrea:OP意思是“原帖发布者”,所以我指的是你 :) - Dan
这仅适用于长度为6个字符的字符串,如要求所示。Dmytro在此分享了任意长度字符串的答案(链接:https://dev59.com/mnVC5IYBdhLWcg3w2lCI#1909960)。 - Cees Timmerman
显示剩余2条评论

61

6
这不起作用。你想到的是“温和贪婪令牌”习语,但点号必须放在预查之后而不是之前。参见此问题。但是,对于这个任务来说,该方法太过复杂了。 - Alan Moore
1
@AlanMoore 实际上,对于这种用例,它几乎可以工作。但是,如果some text在行首,它将返回错误的结果。 - Zenexer
2
@Zenexer,就是这个意思。如果点号放在前瞻之后而不是之前,它就可以完美地工作。 - Alan Moore
请查看Tempered Greedy Token - 在负向先行断言之前放置点有何不同,了解为什么这个答案是错误的。 - Wiktor Stribiżew
链接已经损坏:"无法连接。Firefox 无法与 www.lisnichenko.com 服务器建立连接。" - Peter Mortensen
显示剩余2条评论

45
PCRE和类似变体中,您实际上可以创建一个匹配不包含任何值的任何行的正则表达式:
^(?:(?!Andrea).)*$

这被称为温和的贪婪令牌。缺点是性能不佳。

2
这是Tempered Greedy Token的长形式。只需在第二个lookahead后加上点(或[\s\S],在JavaScript中非常有用),您就不需要第一个点:^(?:(?!Andrea).)*$ - Alan Moore
1
@AlanMoore 很好!我找不到任何像那样有效的已有模式,所以我自己想出了一个。你应该把它作为你自己的答案提供,而不是让我采纳你的答案。 - Zenexer
1
没关系,已经有很多好的答案了。而且你应该因为自己创造了这个习语而受到赞誉。干杯! - Alan Moore
好的,恢复此内容并尝试清理它。 - Zenexer
谢谢!这个答案真的很有用,因为我一直在努力理解Black格式化程序如何处理提供给--exclude的模式。我能够将其设置为忽略除了几个目录中的文件之外的所有内容。 - vintprox
显示剩余3条评论

13

正则表达式实现的功能和语法都很重要。

你可以使用先行断言。以Python为例,

import re

not_andrea = re.compile('(?!Andrea)\w{6}', re.IGNORECASE)

拆解一下:

(?!Andrea) 表示“如果接下来的 6 个字符不是 'Andrea',则匹配”; 如果是,则不匹配

\w 表示“单词字符” - 字母数字字符。这相当于类 [a-zA-Z0-9_]。

\w{6} 表示恰好六个单词字符。

re.IGNORECASE 表示将排除“Andrea”、“andrea”、“ANDREA”等内容...

另一种方法是使用您的程序逻辑 - 使用所有未匹配 Andrea 的行并将它们通过第二个正则表达式检查是否有六个字符。 或者首先检查至少有六个单词字符,然后检查它是否与 Andrea 不匹配。


8

Negative lookahead assertion

(?!Andrea)

这不完全是一个反向匹配,但这是您可以直接使用正则表达式来做的最好方式。不过,并非所有平台都支持它们。

1
在提问者澄清之前,我认为匹配不必从行首开始。那么为什么要使用 ^ 符号呢? - Hamish Downer
因为我理解他想要在行开头进行检查,所以根据给出的澄清进行了编辑。 - Vinko Vrsalovic

6
如果您想在RegexBuddy中完成此操作,有两种方法可以获取不符合正则表达式的所有行的列表。
在测试面板上的工具栏上,将测试范围设置为“逐行”。这样做后,一个名为“列出所有没有匹配项的行”的项目将出现在同一工具栏上的“列出所有”按钮下方。(如果您没有看到“列出所有”按钮,请单击主工具栏中的“匹配”按钮。)
在GREP面板上,您可以打开“基于行”和“反转结果”复选框,以获取在您正在搜索的文件中不匹配的行的列表。

5

我刚想出了这种方法,虽然它可能会占用大量硬件资源,但它是有效的:

您可以通过一个空字符串替换所有与正则表达式匹配的字符。

这是一行代码:

notMatched = re.sub(regex, "", string)

我使用这种方法是因为我被迫使用非常复杂的正则表达式,并且无法在合理的时间内翻转它的每个部分。

这将仅返回字符串结果,而不是任何匹配对象!


什么语言?Python? - Peter Mortensen

5

(?! 在实践中非常有用。虽然严格来说,从前向后查找不是按照数学定义规定的正则表达式。

您可以手动编写反向正则表达式。

这里有一个程序可以自动计算结果。 其结果是机器生成的,通常比手写的更复杂。但是该结果有效。


3
如果您有可能对相反的两个正则表达式进行匹配,并将它们拼接在一起,那么您可以使用两个捕获组来首先捕获您的正则表达式之前的所有内容: ^((?!yourRegex).)* 然后捕获您的正则表达式之后的所有内容: (?<=yourRegex).* 这适用于大多数正则表达式。但是我发现一个问题,即当我在末尾使用量词如{2,4}时,就需要创造性地解决问题了。

-4
在 Perl 中,您可以这样做:
process($line) if ($line =~ !/Andrea/);

6
语法不正确。我认为你的意思是当$line !~ /Andrea/时处理($line)。 - dland

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