【问题标题】:Why does \K appear to consume a character in base R's gsub为什么 \K 似乎消耗了基本 R 的 gsub 中的一个字符
【发布时间】:2017-06-14 23:09:20
【问题描述】:

这是一个示例字符串:'bcadefgh'。
我希望我可以匹配除 'cad' 之外的所有内容,使用以下模式:'\\wa\\w\\K|\\w'。
如果我想替换不是'cad' 的所有内容,我可以使用gsub,就像gsub('\\wa\\w\\K|\\w', '', 'bcadefgh', perl = TRUE),但是这会输出'cade'

gsub(pattern = '\\wa\\w\\K|\\w', replacement = '', 'bcadefgh', perl = TRUE)
# [1] "cade"

我的期望是 \K 会在 'cad' 中的 'd' 之后导致重置,类似于 '\\wa\\w(*SKIP)(*F)|\\w',因此,应该匹配并替换以下 'e'。相反,在我看来 \K 正在消耗一个字符并在“e”之后重新启动匹配过程。我是不是误会了?

使用regex101 (flavor: pcre),我得到了我所期望的:除了“cad”之外的所有内容都匹配。

使用ore 包(Oniguruma 正则表达式,而不是 pcre),我也得到了预期的输出:

ore::ore_subst(regex = '\\wa\\w\\K|\\w',
               replacement = '',
               text = 'bcadefgh',
               all = TRUE)
# [1] "cad"

如果我返回 gsub,但对 'a' 后面的单词字符使用零宽度正向预测而不是使用它((?=\\w) 而不是 \\w):

gsub('\\wa(?=\\w)\\K|\\w', '', 'bcadefgh', perl = TRUE)
# [1] "cad"

我得到了想要的输出,但我不清楚为什么会这样。

这种行为的解释是什么?

【问题讨论】:

  • 当您想在匹配时跳过字符串的某些部分时,(*SKIP)(*F) 方法是最合适的,而不是 \K ,这是一种积极向后看的消费版本。此外,像你这样的模式可以重写为纯 TRE 正则表达式,gsub('(\\wa\\w)?\\w', '\\1', 'bcadefgh'),不需要 PCRE 正则表达式。
  • @WiktorStribiżew 感谢您的意见。这只是一个例子,说明了我对 \\K 行为方式的困惑。这个问题是针对 question 和那里的一些 cmets 而出现的,而不是需要针对这个特定示例的解决方案。

标签: r regex pcre


【解决方案1】:

Advancing After a Zero-Length Regex Match:

R 和 PHP 中的正则表达式函数是基于 PCRE 的,因此它们避免了 通过像 PCRE 那样的回溯而陷入零长度匹配。 但是 R 中用于搜索和替换的 gsub() 函数也会跳过 零长度匹配前一个非零长度的位置 匹配结束,就像 Python 一样。

引擎在遇到零长度匹配方面有所不同。乍一看,似乎在 hello 这样的输入字符串上运行像 \s*|\S+ 这样的正则表达式应该返回 2 个匹配项:

第一个匹配项(h 之前的零长度匹配项):

¦h e l l o 
^

第二次匹配(整个单词):

hello 
>>>>>

人类是这样想的。从正则表达式引擎的角度来看,这不是真的。像 PCRE 这样的著名引擎会返回三个匹配项(两个前一个匹配项和一个零长度匹配项,\s* 在最后一个字符之后)但 javascript 或 Python 返回 6 个。因为当零长度时引擎会跳过下一个非常直接的单个字符找到匹配项。

¦h¦e¦l¦l¦o¦ 
^ ^ ^ ^ ^ ^

所以引擎仅对\s* 部分感到满意​​。 gsub 的工作方式相同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-30
    • 1970-01-01
    • 2021-01-28
    • 1970-01-01
    • 1970-01-01
    • 2013-01-11
    相关资源
    最近更新 更多