R: 在环视中使用环视

33

我需要匹配任何在两个不同元音字母之前的'r'。例如,'our'或'pear'将匹配,但'bar'或'aar'则不会。我已经成功地匹配了两个不同的元音字母,但我仍然无法将其作为后续'r'的回溯条件(...)。使用(?<=...)r...\\Kr都没有得到结果。有什么想法吗?

x <- c('([aeiou])(?!\\1)(?=(?1))')
y <- c('our','pear','bar','aar')
y[grepl(paste0(x,collapse=''),y,perl=T)]
## [1] "our"  "pear"`

6
跳过(skip)和失败(fail)动词的典型用例:https://regex101.com/r/qC9kO2/1 - HamZa
一种暴力方法:combos<-combn(c("a","e","i","o","u"),2);grepl(paste0("(",paste(c(paste0(combos[1,],combos[2,]),paste0(combos[2,],combos[1,])),collapse="|"),")r"),y)。非常丑陋,不认为它足够好作为答案 :) - nicola
@HamZa,为什么不回答呢? - Pouya
@Pouya 我只是在玩而已。那只是一条评论,不是完整的答案。你可以加以解释并将其发布为答案。 - HamZa
1
感谢@HamZa提供的解决方案和解释! - dasf
4个回答

21

这两种解决方案似乎都可行:

第一种是“为什么不”的方式:

x <- '(?<=a[eiou]|e[aiou]|i[aeou]|o[aeiu]|u[aeio])r'
y[grepl(x, y, perl=T)]

\K 方法:

x <- '([aeiou])(?!\\1)[aeiou]\\Kr'
y[grepl(x, y, perl=T)]

为什么不这样做(可能更有效,因为它在搜索“r”之前):

x <- 'r(?<=a[eiou]r|e[aiou]r|i[aeou]r|o[aeiu]r|u[aeio]r)'

或者快速排除未在两个元音字母之前的“r”(而不需要测试整个交替)。
x <- 'r(?<=[aeiou][aeiou]r)(?<=a[eiou]r|e[aiou]r|i[aeou]r|o[aeiu]r|u[aeio]r)'

1
谢谢!第二个选项看起来很不错。 - dasf
2
我们也可以使用递归来缩短模式。不太适合新手:([aeiou])(?!\\1)(?1)\\Kr - HamZa
1
@HamZa:也不是很有用,但我们可以这样做。 - Casimir et Hippolyte
1
我删除了 c()paste(collapse=""),因为它们似乎是不必要的。如果需要这些元素,请随时回滚。 - Tyler Rinker
1
@dasf 我能理解你为什么想这样做。但如果这是你唯一要做的事情,可能更短、更少出错的方法就是将该组写两次。否则,你将处理更多的引号和逗号,很容易搞砸。不过,这种方法维护起来并不那么容易,因为如果你将来想要更改它,你需要在两个位置进行更改,而不仅仅是一个位置。所以无论哪种方式都有权衡。话虽如此,如果你在超过两个地方使用它,那么这可能是正确的方法。 - Dason
显示剩余5条评论

15
如评论中HamZa所指出的,使用skip和fail动词是实现我们想要的一种方法。基本上,我们告诉它忽略那些有两个相同元音字母后跟着“r”的情况。
# The following is the beginning of the regex and isn't just R code
# the ([aeiou]) captures the first vowel, the \\1 references what we captured
# so this gives us the same vowel two times in a row
# which we then follow with an "r"
# Then we tell it to skip/fail for this
([aeiou])\\1r(*SKIP)(*FAIL)

现在我们告诉它跳过那些情况,然后告诉它“或者出现两个元音字母后面跟着一个'r'的情况”,由于我们已经排除了这两个元音字母相同的情况,这将得到我们想要的结果。
|[aeiou]{2}r

将它们组合起来,我们最终得到:
y <- c('our','pear','bar','aar', "aa", "ae", "are", "aeer", "ssseiras")
grep("([aeiou])\\1r(*SKIP)(*FAIL)|[aeiou]{2}r", y, perl = TRUE, value = TRUE)
#[1] "our"    "pear"    "sseiras"

谢谢您的解释。我有一种感觉这将在今后非常有用。 - dasf

6

这是一个不太优雅的解决方案:

y[grepl("[aeiou]{2}r", y, perl=T) & !grepl("(.)\\1r", y, perl=T)]

可能存在一些边缘情况的失败,其中第一组匹配与第二组不同位置相匹配(需要考虑这一点),但可以作为起点。


1
我喜欢这种方法。大多数情况下,我们可以通过将其分解为两个或更多进程(正则表达式)来简化流程。+1 - HamZa
我认为只有(?!(.)\1)[aeiou]{2}r会更好 -模式稍微复杂一些,但代码要简单得多。 - Kobi
@Kobi,你的模式不能做到相同的事情,会匹配像“bcaar”这样的东西。正确的方法是像Casimir展示的那样。 - BrodieG
你非常错误。我认为这是你的代码问题,它不会接受 bbraer(正如你在回答中所说)。 - Kobi
@Kobi,非常有趣,将其视为向后查找而不是向前查找。在精神上与Casimir的类似,对吧? - BrodieG
是的。我会把它添加到答案中,但它与Casimir和你的答案太相似了。 - Kobi

4

另一个通过负向前瞻断言实现的。

> y <- c('our','pear','bar','aar', "aa", "ae", "are", "aeer", "ssseiras")
> grep("(?!(?:aa|ee|ii|oo|uu)r)[aeiou][aeiou]r", y, perl=TRUE, value=TRUE)
[1] "our"      "pear"     "ssseiras"

> grep("(?!aa|ee|ii|oo|uu)[aeiou][aeiou]r", y, perl=TRUE, value=TRUE)
[1] "our"      "pear"     "ssseiras"

(?!aa|ee|ii|oo|uu) 表示匹配的第一个和第二个字符不会是 aaee 或 .... 或 uu。因此,这个 [aeiou][aeiou] 可以匹配任何两个元音字母,但不会重复。这就是为什么我们首先设置了这个条件。 r 匹配跟在元音字母后面的 r。


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