正则表达式 - 如何匹配除特定模式外的所有内容

175

我该如何编写正则表达式以匹配任何不符合特定模式的字符串?我面临一个情况,需要匹配一个(A和~B)模式。


PCRE 对于这个问题是最好的选择:请参见 Regex Pattern to Match, Excluding when…/Except between。我删除了 findstr 标签,因为所有答案都不适用于该标签。 - Wiktor Stribiżew
8个回答

198

你可以使用前瞻断言:

(?!999)\d{3}

这个例子匹配了三位数,但不包括 999


如果您的正则表达式引擎没有这个特性(请参见正则表达式引擎比较),您可能需要自己构建一个只具备基本语法的正则表达式。

一个兼容基本语法的正则表达式可以是:

[0-8]\d\d|\d[0-8]\d|\d\d[0-8]

这也匹配任何不是999的三位数字序列。


1
Look-ahead不是标准的正则表达式语法,它是Perl扩展的一部分,只能在Perl、PCRE(Perl-Compatible RegEx)或其他非标准实现中使用。 - Juliano
10
现代大多数语言都支持它,虽然这不算标准。现在有哪种语言不支持前瞻呢? - Bryan Oakley
1
没错。但是大多数正则表达式都支持这个功能(请参见http://www.regular-expressions.info/refflavors.html)。 - Gumbo
结果发现 Windows 的 findstr 函数只支持纯 DFA 风格的正则表达式,所以我需要完全用不同的方法来解决它。不过你还是能得到答案。 - notnot
1
我认为最后一个正则表达式也无法匹配009、019等。 - Sebastian Viereck
1
标准 C 词法分析器不使用 PCRE :-( - pieman72

32
如果您想在字符串中匹配单词A,但不匹配单词B。例如:如果您有一个文本:
1. I have a two pets - dog and a cat
2. I have a pet - dog

如果您想搜索拥有狗作为宠物并且没有猫的文本行,您可以使用以下正则表达式:
^(?=.*?\bdog\b)((?!cat).)*$

它只会找到第二行:
2. I have a pet - dog

他在问题中没有提到,但是OP实际上正在使用DOS的findstr命令。它只提供了您期望在正则表达式工具中找到的一小部分功能;前瞻不在其中。(我刚刚自己添加了[tag:findstr]标签。) - Alan Moore
2
嗯,是的,我现在在他的一条评论中找到了。我在标题中看到了正则表达式。无论如何,如果有人像我一样在搜索正则表达式时找到了这篇文章,也许对某些人有帮助:) 感谢评论。 - Aleks

15

使用主语言反转匹配结果的布尔值,并与模式进行比较。这样做会使代码更易读且更易于维护。


1
然后我最终得到的是(A或B),而不是(A且B)。这并不能解决我的问题。 - notnot
1
伪代码:String toTest; if (toTest.matches(A) AND !toTest.matches(B)) { ... } - Ben S
我应该更清楚地表达 - 这些部分并不是完全独立的。如果A匹配字符串的一部分,那么我们关心的是~B是否匹配其余部分(但不一定是整个字符串)。这是针对Windows命令行findstr函数的,我发现它受限于真正的正则表达式,所以这个问题无关紧要了。 - notnot

8

我重新回复这个古老的问题是因为它有一个简单的解决方案,但没有被提到。(在正则表达式奖励任务的研究过程中找到了你的问题。)

我面临的情况是匹配(A和~B)模式。

基本的正则表达式非常简单:B|(A)

您只需忽略整体匹配并检查组1捕获,其中包含A。

一个示例(带有有关在正则表达式中解析html的免责声明):A是数字,B是<a tag中的数字

正则表达式:<a.*?<\/a>|(\d+)

演示(查看右下方的组1)

参考资料

如何匹配除了 s1、s2、s3 之外的模式

如何匹配除了...


这听起来太好了!不幸的是,这个解决方案并不通用,在Emacs中即使将\d替换为[[:digit:]]也会失败。第一个参考提到它只适用于Perl和PHP:“有一种使用特定于Perl和PHP语法的变体可以实现相同的功能。” - miguelmorin

4
一个正则语言的补集也是一个正则语言,但是要构建它,你需要首先构造这个正则语言的DFA,然后将任何有效状态的转换变成错误转换。可以参考这个页面进行学习。该页面没有说明的是它将/(ac|bd)/转换为/(a[^c]?|b[^d]?|[^ab])/。从DFA转换回正则表达式并非易事。如果你可以在代码中保留正则表达式而改变语义,像之前建议的那样,那么这个任务将更容易一些。

2
如果我在处理实际的正则表达式,那么这一切都将无关紧要。现在,正则似乎是指大多数语言支持的模式匹配中的模糊 CSG-ish (?) 空间。由于我需要匹配 (A and ~B),因此没有办法去除否定并仍然一步完成所有操作。 - notnot
如果findstr除了真正的DFA正则表达式之外还有其他功能,那么如上所述的前瞻将会完成它。整个过程有点奇怪,我不知道为什么我必须以这种命令行(批处理)方式执行此操作。这只是我的手被绑住的又一个例子。 - notnot
1
@notnot:您正在使用来自Windows的findstr吗?那么您只需要 /v。例如: findstr A inputfile | findstr /v B > outputfile.txt 第一个命令匹配所有包含A的行,而第二个命令匹配所有不包含B的行。 - Juliano
谢谢!这正是我所需要的。虽然我没有以那种方式提问,但我仍然会将答案给Gumbo,以便获得更普遍的答案。 - notnot

2

正则表达式 - re

str.split(/re/g) 

将返回除模式外的所有内容。

此处进行测试。


你可能需要提到你需要再次连接它们。 - tomdemuyt
一个类似的方法是使用 replacestr.replace(/re/g, ''),这样就不需要重新连接它们了。另外,如果你加上一个漂亮的尾随 \s?,比如 str.replace(/\re\s?/g, ''),那么你就可以摆脱任何在字符串中间被替换而导致的重复空格。 - jakecraige

0
(B)|(A)

然后使用第二组捕获的内容...


他需要捕获的不是B,他的目标不仅仅是忽略所有的B模式。 - hexicle

0

我的回答可能也能解决你的问题:

https://stackoverflow.com/a/27967674/543814

  • 不要使用 Replace,而是使用 Match。
  • 不要使用组 $1,而是读取组 $2
  • 那里将组 $2 设为非捕获组,你应该避免这样做。

示例:

Regex.Match("50% of 50% is 25%", "(\d+\%)|(.+?)");

第一个捕获组指定了您希望避免的模式。最后一个捕获组捕获其他所有内容。只需读取该组,$2


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