正则表达式:是否存在AND运算符?

996

显然,你可以使用 |(管道符号)来表示 OR,但是否有一种方式来表示 AND

具体而言,我想匹配包含某些短语的所有文本段落,但顺序不限。


2
你的意思是要在文本中找到短语,其中每个短语都是给定短语单词的有效排列吗? - Nietzche-jou
3
我把这个放在这里,因为有三到四个答案忽略了它。除非它们以$结尾,否则向前查找不会匹配每个子句的相同长度。一个向前查找可以匹配四个字符,而另一个可以匹配六个。例如, (?=a*)(?=aab) 将匹配 aabaaaaba - Zachary Vance
4
尝试仅使用空格字符作为“AND”运算符。 - user1045737
  1. 我想匹配文本段落。
  2. 包含无序文本。第一条可以有不同的解释。第二条可以用两种方式完成。方式1:(?:(?:(?(1)(?!))\b(phrase1)\b.*?|(?(2)(?!))\b(phrase2)\b.*?)){2},方式2:(?=.*\bphrase1\b)(?=.*\bphrase2\b),在这种情况下,匹配段落的定义未确定。
- user557597
具体来说,我想匹配包含某个短语的文本段落,但顺序不限。这与 | 的作用不同,这使问题变得混乱。| 检查当前位置是否匹配两个模式中的任意一个。它不检查字符串的其余部分是否至少包含其中之一。"and" 对应物将检查当前位置是否同时匹配两个模式;但显然您想搜索字符串并检查所有模式是否在其中某处匹配。 - Karl Knechtel
我想匹配包含特定短语的文本段落,但顺序不限。这句话的意思是什么?“顺序不限”指的是短语中的单词还是字符?它们需要按顺序匹配吗?它们可以重叠吗?它们之间可以有其他文本吗?这个问题根本没有被明确定义。 - Karl Knechtel
15个回答

505

使用非消耗性正则表达式

典型的表示方法(如Perl / Java)是:

(?=表达式)

这意味着“匹配expr ,但在那之后继续在原始匹配点进行匹配。”

您可以使用任意数量的这些表达式,这将是“and”的关系。例如:

(?=匹配此表达式)(?=也匹配这个)(?=还有这个)

如果需要保存其中某些数据,甚至可以在非消耗性表达式内部添加捕获组。


4
perl -e "q{一些东西} =~ /(?=一些)(?=东西)(?=和东西)/ ? print '是' : print '不是'" 会输出 '不是'。 - Robert P
34
应该提到,这个特定的例子被称为正向先行断言。它有比“and”更多的用途。请注意,文本没有被消耗。 - strager
8
使用 (?=) 如此会导致正则表达式永远无法匹配成功。但它确实是与 | 对应的连词。原帖中的问题在于他误以为这样做可以解决他的问题。 - Nietzche-jou
12
perl -e "q{一些东西} =~ /(?=.*一些)(?=.*东西)/ ? print '是的' : print '不'" - kriss
3
请问您能否在您的回答中添加一些Perl代码的简单示例? - Pithikos
显示剩余9条评论

474

你需要像其他回答者所说的那样使用前瞻,但是前瞻必须考虑其目标词和当前匹配位置之间的其他字符。例如:

(?=.*word1)(?=.*word2)(?=.*word3)
第一个正向前瞻中的 .* 可以匹配在 "word1" 之前任意数量的字符。然后,匹配位置被重置,第二个正向前瞻查找 "word2"。再次重置后,最后一部分匹配 "word3";由于它是你要检查的最后一个单词,所以不需要在前瞻中匹配,但这也没有坏处。
为了匹配整个段落,你需要在正则表达式的两端加上锚点,并增加一个最终的 .* 来消耗剩余的字符。使用 Perl 风格的表示法,可以写成:
/^(?=.*word1)(?=.*word2)(?=.*word3).*$/m

'm'修饰符用于多行模式,它允许^$匹配段落边界(在正则表达式中称为"行边界")。 在这种情况下,关键是不要使用's'修饰符,因为它允许点元字符匹配换行符以及所有其他字符。

最后,您需要确保匹配整个单词而不仅仅是长单词的片段,因此您需要添加单词边界:

/^(?=.*\bword1\b)(?=.*\bword2\b)(?=.*\bword3\b).*$/m

9
完全正确 - 这方面还有一个教程!http://ocpsoft.org/tutorials/regular-expressions/and-in-regex/ - Lincoln
11
非常感谢。这会产生影响。 - Henadzi Rabkin
4
+1 对于清晰简洁的回答,展示了 lookahead 的最佳用途之一(与使用它来计算密码匹配百分比等用途不同)。 :) - zx81
1
@Liam:MySQL使用POSIX ERE风格,因此不支持Perl兼容的正则表达式。它在性能方面牺牲了一些功能,这对我来说似乎是合理的。更多信息请参见此处:http://www.regular-expressions.info/mysql.html。 - Alan Moore
4
如果您在JavaScript中的正则表达式引擎中有换行符,则应将“.*”替换为“[\s\S]*”,因为“.”无法匹配换行符,也无法通过修饰符进行匹配。 - Wesley Smith
显示剩余2条评论

55

看这个例子:

我们有两个正则表达式 A 和 B,我们想要匹配它们两个,伪代码如下:

pattern = "/A AND B/"

以下是不使用 AND 运算符的写法:

pattern = "/NOT (NOT A OR NOT B)/"

在 PCRE 中:

"/(^(^A|^B))/"

regexp_match(pattern,data)

30
在形式逻辑方面这是正确的,但在这里完全没有帮助。在正则表达式中,NOT运算比AND运算更难表达。 - Alan Moore
@marvin_dpr 在CMake中这对我很有效,而其他建议(?=expr)不行。这似乎取决于实现。 - Melebius
44
在正则表达式语法中,^ 不是表示字符串的开头吗? - Lambda Fairy
4
通常情况下,在正则表达式中,^ 只在字符类的开头表示取反。除非 CMake 做了一些特别奇怪的事情(以至于称他们的模式匹配语言为 "正则表达式" 可能会被视为误导或不正确),否则我猜测它对你起作用只是偶然事件。 - tripleee
6
这个绝对错误的回答怎么会得到这么多的赞?在 /(^(^A|^B))/ PCRE 中,^ 的意思是“行首”,而不是否定。也许可以通过负向先行断言(?!…), 如(?!(?!A)|(?!B)))运气好一些,但肯定不能用 ^ - Sasha

44

RegExp语法中的AND运算符是隐含的。
OR运算符必须用管道符号指定。
下面是一个RegExp示例:

var re = /ab/;

意思是字母a字母b。它也适用于组合:

var re = /(co)(de)/;

这意味着组co de。将(隐含的)"AND"替换为"OR"需要以下行:

这表示组code。如果要用"OR"替换隐式的"AND",则需要以下几行:

var re = /a|b/;
var re = /(co)|(de)/;

41
很不幸,这并不是OP所要求的。这会按照给定的顺序查找任何内容,而他们想以任意顺序查找它们。请查看下面由http://stackoverflow.com/users/20938/alan-moore提供的正确答案。 - JESii
2
@JESii 感谢您的观点,您是正确的,我误解了Hugoware的问题,我特别关注他的第一句话。正确的答案是使用前瞻运算符,就像AlanMoore所写的那样。无论如何,我认为有人可能会发现我的澄清有用,因为它已经得到了赞同,所以我不会把一切都扔掉。问候。 - yodabar
“means the letter a AND the letter b”并不是这样的;它意味着a紧接着b。相比之下,|同一位置检查两个模式。类似的版本是使用前瞻来测试是否匹配了两个模式;但是那么就不清楚匹配应该包含什么了。 - Karl Knechtel

30
你可以使用正则表达式来做到这一点,但可能你会想要其他的方法。例如使用几个正则表达式,并将它们组合在一个if子句中。
你可以使用标准正则表达式枚举所有可能的排列,像这样(匹配任意顺序的a、b和c):
(abc)|(bca)|(acb)|(bac)|(cab)|(cba)

但是,如果有多于几个术语,这将生成非常长且可能效率低下的正则表达式。

如果您使用某些扩展的正则表达式版本,例如Perl或Java,则它们有更好的方法来处理这种情况。其他回答建议使用正向预查运算符。


10
我认为你的方法并不比使用3个预查和它们的灾难性回溯更低效。当然,这种方法写起来需要更长的代码,但要注意可以很容易地自动生成该模式。请注意,您可以使用"a(bc|cb)|b(ac|ca)|c(ab|ba)"来改进它以更快地进行匹配。最重要的是,您可以在所有正则表达式环境中使用它。 - Casimir et Hippolyte

15

在你的情况下,无法对多个匹配结果执行AND操作吗?伪代码示例:

regexp_match(pattern1, data) && regexp_match(pattern2, data) && ...

3
我处于这样一种情况:我有一些代码,它是一张规则数据表,其中每个规则都有一个正则表达式模式匹配字符串来测试规则的有效性。在我的情况下,无法进行多个测试,而且在其他人的情况下也常常如此! - Alan Wolfe
@AlanWolfe 我现在正在处理完全相同的情况...所以你已经找到了处理逻辑AND的正确方法吗? - 赣西狠人

13

为什么不使用awk?
使用awk,正则表达式中的AND和OR非常简单明了

awk '/WORD1/ && /WORD2/ && /WORD3/' myfile

11

正则表达式的结构总是隐含了顺序。要完成您想要的,您需要针对不同的表达式多次匹配输入字符串。

使用单个正则表达式无法实现您想要的操作。


从技术上讲,这并不是不可能实现的,但却不值得去实现。虽然我不知道为什么有人会给它点踩... - Robert P
13
也许是因为这不仅可能,而且很简单,只要您的正则表达式支持前瞻。这是一个好选择;现今大多数主流编程语言都支持它们。 - Alan Moore

10
如果您使用Perl正则表达式,可以使用正向前瞻:
例如:
(?=[1-9][0-9]{2})[0-9]*[05]\b

将是大于100且可被5整除的数字。


9

除了被接受的答案

我将为您提供一些实际示例,以便更好地理解。例如,假设我们有以下三行文本:

[12/Oct/2015:00:37:29 +0200] // only this + will get selected
[12/Oct/2015:00:37:x9 +0200]
[12/Oct/2015:00:37:29 +020x]

点击此处查看演示 演示

我们想要做的是选择加号,但只有在它在两个数字后面有一个空格,并且在四个数字之前。这些是唯一的限制条件。我们将使用以下正则表达式来实现:

'~(?<=\d{2} )\+(?=\d{4})~g'

注意,如果你分开表达式,它将给出不同的结果。

或者,也许你想选择标签之间的一些文本……但不包括标签!那么你可以使用:

'~(?<=<p>).*?(?=<\/p>)~g'

对于以下文本:

<p>Hello !</p> <p>I wont select tags! Only text with in</p> 

这里查看演示 演示


哪个答案被接受了?请添加一个链接,以便未来的我。 - James Brown

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