不包含特定字符串的正则表达式

88
我有类似这样的东西
```html

aabbabcaabda

```
为了选择被a包裹的最小组,我使用了这个正则表达式:`/a([^a]*)a/`,它能够很好地工作。
但是当我想要选择被aa包裹的组时,我需要像这样的东西:
```html /aa([^aa]*)aa/ ```
但这并不能起作用,我也不能像这样使用第一个正则表达式:
```html /aa([^a]*)aa/ ```
因为它会在遇到第一个a时结束,而这不是我想要的。
通常来说,有没有办法以与使用`[^a]`表示不包含字符相同的方式来表示不包含字符串
简单来说,我需要选择aa后面跟着任意一个除了aa序列以外的字符,并以aa结尾的组。


@SteveChambers 你意识到这个问题已经超过7年了,并且已经有一个被接受的答案了吗? - Jakub Arnold
1
当然,就我理解而言,这两个观点都不是它不应该链接到重复项的原因。 - Steve Chambers
7个回答

230

通过谷歌的力量,我发现了一个来自2007年的博客文章,其中提供了以下正则表达式,可以匹配不包含特定子字符串的字符串:

^((?!my string).)*$

它的工作方式如下:查找零个或多个(*)字符(.),这些字符不以你的字符串开头(使用负向先行断言(?!)),并且它规定整个字符串必须由这些字符组成(通过使用^和$锚点)。或者换一种方式表达:

整个字符串必须由不以给定字符串开头的字符组成,这意味着该字符串不包含给定的子字符串。


10
根据文档,这是负向先行断言,而不是向后断言。 - Luigi Plinge
完整的正则表达式参考资料:http://www.regular-expressions.info/refadv.html - Juh_
1
问题的确切解决方案是:^aa(?!.*aa.*aa).*aa$,即以 aa 开始,向前查找并丢弃跟随的选择,其中包含 [anything]aa[anything]aa,最后以 aa 结束。 - Juh_
@daleyjem - 是的,没错 - 当你无法指定正则表达式标志(如s,点匹配任何内容 - 包括换行符)时,使用 (\s|\S) 而不是 . 是一种解决方法。 - Grey Panther
1
我想这取决于引擎?根据MDN,在Javascript中点不识别行终止符。SublimeText的“查找/替换”正则表达式也不使用点匹配换行符。 - daleyjem
显示剩余2条评论

19

通常编写一个不包含特定字符串的正则表达式是一件麻烦的事情。我们在计算模型中需要这样做-您可以使用易于定义的NFA,然后将其缩减为正则表达式。不包含“cat”内容的表达式大约有80个字符长度。

编辑:我刚刚完成了,确实是:

aa([^a] | a[^a])aa

这里有一个非常简短的教程。我以前找到过一些很棒的教程,但现在看不到了。


你知道有没有任何解释这个问题的教程吗? - Jakub Arnold
这里有一个很好的正则表达式教程:http://www.regular-expressions.info/。 - Alan Moore
你好,你确定吗?有人能告诉我们我的答案是否有问题吗:https://dev59.com/nXRB5IYBdhLWcg3wHkLi#27060278 - AFract
链接失效..... - lindhe

11

你所需要的是一个非贪婪量词:

regex: /aa.*?aa/

aabbabcaabda   => aabbabcaa

aaaaaabda      => aaaa

aabbabcaabda   => aabbabcaa

aababaaaabdaa  => aababaa, aabdaa
你也可以使用负向先行断言,但在这种情况下,它只是一种更冗长的方式来完成同样的事情。而且,它比gpojd描述的要棘手一些。在点允许消耗下一个字符之前,必须在每个位置应用先行断言。
/aa(?:(?!aa).)*aa/

关于Claudiu和finnw建议的方法,当哨兵字符串只有两个字符长时,它会正常工作,但是对于更长的字符串来说(正如Claudiu所承认的那样),这种方法就太笨重了。


1
我认为我们的方法是唯一适用于非回溯实现(http://swtch.com/~rsc/regexp/regexp1.html)的方法,但是,是的,它非常令人恼火。我只是不太了解正则表达式,不知道这些前瞻的东西=)。 - Claudiu
大多数现代正则表达式引擎,尤其是内置于编程语言中的引擎,都是回溯型、NFA(非确定有限自动机)类型的。即使是功能最少的JavaScript也支持前瞻和勉强量词。http://www.regular-expressions.info/refflavors.html - Alan Moore

7
/aa([^a]|a[^a])*aa/

6
我不确定这是否是标准结构,但我认为你应该看一下“负向先行断言”(写作:“?!”而不带引号)。 它比本主题中的所有答案都要简单得多,包括被接受的答案。
例如: 正则表达式:“^(?!123)[0-9]*\w” 捕获以数字开头后跟字母的任何字符串,除非“这些数字”是123。
请参阅http://msdn.microsoft.com/en-us/library/az24scfc%28v=vs.110%29.aspx#grouping_constructs(微软页面,但相当全面)了解先行/后行查找。
附注:这对我很有效(.Net)。但如果我有什么错误,请告诉我们。我发现这个结构非常简单和有效,所以我对被接受的答案感到惊讶。

4
在以下代码中,我需要将所有对JS文件的引用添加一个GET参数,但有一个例外。请参考下面的代码:

在下面的代码中,除了一个例外,我需要为所有JS文件的引用添加一个GET参数。

<link rel="stylesheet" type="text/css" href="/login/css/ABC.css" />
<script type="text/javascript" language="javascript" src="/localization/DEF.js"></script>
<script type="text/javascript" language="javascript" src="/login/jslib/GHI.js"></script>
<script type="text/javascript" language="javascript" src="/login/jslib/md5.js"></script>
sendRequest('/application/srvc/EXCEPTION.js', handleChallengeResponse, null);
sendRequest('/application/srvc/EXCEPTION.js",handleChallengeResponse, null);

这是使用的Matcher:

(?<!EXCEPTION)(\.js)

它的作用是查找所有出现的".js",如果它们前面有"EXCEPTION"字符串,则将该结果从结果数组中丢弃。这就是所谓的负向回溯。由于我花了一天的时间才找到如何做到这一点,所以我想分享一下。


3
".*[^(\\.inc)]\\.ftl$"

在Java中,这将找到所有以“.ftl”结尾的文件,但不包括以“.inc.ftl”结尾的文件,这正是我想要的。

3
inc拆分为inc。因此,使用"a.i.ftl".matches(".*[^(\\.inc)]\\.ftl$")"a.inc.ftl".matches(".*[^(\\.inc)]\\.ftl$")都会返回false。 - user1521536

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