R: (*SKIP)(*FAIL) 对于多个模式

R: (*SKIP)(*FAIL) for multiple patterns

给定 test <- c('met','meet','eel','elm'),我需要一行代码来匹配不在 'me' 或 'ee' 中的任何 'e'。我写了 (ee|me)(*SKIP)(*F)|e,它确实排除了 'met' 和 'eel',但不包括 'meet'。这是因为 | 是独占的还是?无论如何,有没有解决方案 returns 'elm'?

郑重声明,我知道我也可以 (?<![me])e(?!e),但我想知道 (*SKIP)(*F) 的解决方案是什么以及为什么我的行是错误的。

您可以在您的第一个模式中添加 \w* 以帮助引擎处理更多数据,告知 eeme 可以出现在字符串的开头、中间或结尾.

您可以像这样使用正则表达式:

\w*(ee|me)\w*(*SKIP)(*FAIL)|e

R 正则表达式是,

> test <- c('met','meet','eel','elm')
> gsub("\w*(?:ee|me)\w*(*SKIP)(*FAIL)|e", "fi", perl=TRUE, test)
[1] "met"  "meet" "eel"  "film"

> gsub('(?:^.*[me]e)(*SKIP)(*FAIL)|e', 'fi', test, perl=T)
[1] "met"  "meet" "eel"  "film"

Working demo

您需要一个 preceding/following 边界来强制正则表达式引擎不重试子字符串。

gsub('\w*[em]e\w*(*SKIP)(?!)|e', '', test, perl=T)

或者正如@CasimiretHippolyte 指出的那样——前面有一个可选的 "e" ...

gsub('(?:ee|mee?)(*SKIP)(?!)|e', '', test, perl=T)

根据评论更新(使用量词(对于其他情况)):

gsub('[em]e+(*SKIP)(?!)|e', '', test, perl=T)

注意: 我决定使用 (?!) 而不是 (*F),后者也用于强制正则表达式失败。

(?!)    # equivalent to ( (*FAIL) or (*F) - both synonyms for (?!) ), 
        # causes matching failure, forcing backtracking to occur

总的来说,语法可以写成(*SKIP)(*FAIL)(*SKIP)(*F)(*SKIP)(?!)

这是 (*SKIP)(*F) 的正确解决方案:

(?:me+|ee+)(*SKIP)(*FAIL)|e

Demo on regex101,使用以下测试用例:

met
meet
eel
elm
degree
zookeeper
meee

只有elm中的edegree中的第一个ezookeeper中的最后一个e匹配。

由于ee中的e是禁止的,所以m之后的任何e都是禁止的,并且连续[=的子串中的任何e 18=] 被禁止。这解释了子模式 (?:me+|ee+).

虽然我知道此方法不可扩展,但至少在逻辑上是正确的。

其他方案分析

解决方案 0

(ee|me)(*SKIP)(*F)|e

我们以meet为例:

meet        # (ee|me)(*SKIP)(*F)|e
^           # ^

meet        # (ee|me)(*SKIP)(*F)|e
  ^         #        ^

meet        # (ee|me)(*SKIP)(*F)|e
  ^         #               ^
            # Forbid backtracking to pattern to the left
            # Set index of bump along advance to current position

meet        # (ee|me)(*SKIP)(*F)|e
  ^         #                  ^
            # Pattern failed. No choice left. Bump along.
            # Note that backtracking to before (*SKIP) is forbidden,
            # so e in second branch is not tried

meet        # (ee|me)(*SKIP)(*F)|e
  ^         # ^
            # Can't match ee or me. Try the other branch

meet        # (ee|me)(*SKIP)(*F)|e
   ^        #                    ^
            # Found a match `e`

问题是由于me消耗了第一个e,所以ee匹配失败,留下第二个e可以匹配。

解决方案 1

\w*(ee|me)\w*(*SKIP)(*FAIL)|e

这将跳过所有包含 eeme 的单词,这意味着它将无法匹配 degreezookeeper.

中的任何内容

Demo

解决方案 2

(?:ee|mee?)(*SKIP)(?!)|e

与解决方案0类似的问题。当连续3个e时,前2个emee?匹配,剩下第三个e可用用于匹配。

解决方案 3

(?:^.*[me]e)(*SKIP)(*FAIL)|e

这将丢弃直到最后一个 meee 的输入,这意味着最后一个 meee 之前的任何有效 e不会被匹配,比如 degree.

中的第一个 e

Demo