了解 R 正则表达式中的 lookahead。

10

我试图使用多个lookahead来在R Perl类型的正则表达式中使用grep模拟AND运算符。但是,我不理解我看到的输出。以下是一个示例代码块:

a <- c("abcxyz", "abcdef", "defxyz", "abcdefxyz", "xyzdefabc")
grep("(?<=abc)(?=xyz)", a, ignore.case=TRUE, perl=TRUE)  # returns 1
grep("(?=abc)(?=xyz)", a, ignore.case=TRUE, perl=TRUE)  # returns integer(0)

第二行表明字符串中的位置在abc和xyz之间,并匹配'abcxyz'。为什么它不匹配'abcdefxyz'?

在第三行中,我尝试输出1、4和5,但返回未找到。为什么会发生这种情况?

我正在使用下面的另一种解决方案,但我想使用lookaheads来处理多个lookaheads时的顺序。

grep("abc.*xyz|xyz.*abc", a, ignore.case=TRUE, perl=TRUE)  # returns 1 4 5 as expected

在第二个选项中,您使用了?=而不是?<=。也许您需要使用grep("(?<=abc).*(?=xyz)", a, ignore.case=TRUE, perl=TRUE) - akrun
@akrun 我试图避免同时使用向后和向前查找,因为它会强制排序,而我不想要。我已经编辑了示例代码以使其更清晰。我对这个例子的期望是:http://www.rexegg.com/regex-lookarounds.html - Naumz
1个回答

17

(?<=abc)(?=xyz) 正则表达式只匹配位于 abcxyz 之间的位置(字符串中的位置)。它会在 abcxyz 中找到匹配,但不会在 abcdefxyz 中找到匹配,因为 xyz 不是紧跟着 abc

(?=abc)(?=xyz) 模式永远不会匹配任何内容,因为它匹配的是一个字符串中后面跟着一个应该同时等于 abcxyz 的三个字母序列的位置,这是不可能的。

你要找的是

^(?=.*abc)(?=.*xyz)

或者,为了支持多行输入,请添加DOTALL修改器(?s)(使.也可以匹配换行符):

或者,为了支持多行输入,请添加DOTALL修改器(?s)(使.也可以匹配换行符):

(?s)^(?=.*abc)(?=.*xyz)

这些将匹配一个字符串,该字符串以任何顺序同时包含abcxyz

参见R演示

a <- c("abcxyz", "abcdef", "defxyz", "abcdefxyz", "xyzdefabc")
grep("^(?=.*abc)(?=.*xyz)", a, ignore.case=TRUE, perl=TRUE)
## => [1] 1 4 5

1
感谢您的回答!我现在认为我明白了自己困惑的原因。使用 "(?=abc)(?=xyz)",第一部分将位置设置为刚好在 abc 出现之前,所以第二部分无法匹配到该位置的 xyz。这是正确的吗? - Naumz
1
(?=abc) 检查 abc 之前的位置,一旦找到该位置,正则表达式引擎会尝试在那个确切的位置找到 xyz(因为 (?=...) 是一个零宽度断言,当前瞻模式匹配时不会移动正则表达式索引)。 abc 不能是 xyz,因此找不到匹配项。 - Wiktor Stribiżew
@WiktorStribiżew,我在查找正则表达式时偶然来到这里。我有一个疑问,在正则表达式中,"^"不是表示字符串的起始位置吗?所以正则表达式应该尝试查找以'abc'作为前三个字符序列的模式,但你说你的正则表达式将匹配'abc'和'xyz',无论顺序如何,那么这是否意味着正则表达式一直到字符串'xyzdefabc'中的'abc',找到末尾的'abc',然后回到起点并找到'xyz'? - Karthik S
2
@KarthikS ^ 匹配字符串的开头。我的 ^(?=.*abc)(?=.*xyz) 正则表达式匹配包含 abcxyz 的任意顺序的字符串,同时正则表达式索引保持在字符串的开头,因为先行断言是非消费模式。首先检查 .*abc,找到匹配项后返回 true(表示是的,继续尝试匹配表达式的其余部分),然后从字符串开头开始搜索 .*xyz。由于找到了匹配项并且返回值为 true,因此匹配成功。 - Wiktor Stribiżew

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