24

我很确定我在这里遗漏了一些明显的东西,但我不能让 R 使用非贪婪的正则表达式:

> library(stringr)
> str_match('xxx aaaab yyy', "a.*?b")                                         
     [,1]   
[1,] "aaaab"

基函数的行为方式相同:

> regexpr('a.*?b', 'xxx aaaab yyy')
[1] 5
attr(,"match.length")
[1] 5
attr(,"useBytes")
[1] TRUE

我希望比赛与http://stat.ethz.ch/R-manual/R-devel/library/base/html/regex.htmlab中的“贪婪”评论一样:

默认情况下,重复是贪婪的,因此使用最大可能的重复次数。可以通过附加将其更改为“最小”?到量词。(还有更多允许近似匹配的量词:请参阅 TRE 文档。)

有人可以解释一下发生了什么吗?

更新。疯狂的是,在某些其他情况下,非贪婪模式的行为符合预期:

> str_match('xxx <a href="abc">link</a> yyy <h1>Header</h1>', '<a.*>')
     [,1]                                          
[1,] "<a href=\"abc\">link</a> yyy <h1>Header</h1>"
> str_match('xxx <a href="abc">link</a> yyy <h1>Header</h1>', '<a.*?>')
     [,1]              
[1,] "<a href=\"abc\">"
4

2 回答 2

30

困难的概念,所以我会尽力而为......如果有点令人困惑,有人可以随意编辑和解释得更好。

从左到右搜索与您的模式匹配的表达式。是的,以下所有字符串aaaabaaabaabab都与您的模式匹配,但是aaaab从最左边开始的字符串就是返回的字符串。

所以在这里,你的非贪婪模式不是很有用。当非贪婪模式出现时,也许这个其他示例将帮助您更好地理解:

str_match('xxx aaaab yyy', "a.*?y") 
#      [,1]     
# [1,] "aaaab y"

这里所有的字符串aaaab y, aaaab yy,都aaaab yyy与模式匹配并从相同的位置开始,但由于非贪婪模式,返回了第一个字符串。


那么你能做些什么来抓住最后一个ab呢?用这个:

str_match('xxx aaaab yyy', ".*(a.*b)")
#      [,1]        [,2]
# [1,] "xxx aaaab" "ab"

它是如何工作的?通过在前面添加一个贪婪模式.*,您现在强制该过程将最后一个可能的a放入捕获的组中。

于 2013-05-16T01:37:49.473 回答
9

问题匹配两个字符串之间的最短窗口。@flodel 正确地提到正则表达式引擎正在从左到右解析字符串,因此所有匹配项都是leftmost。贪婪和懒惰只适用于右边的边界:贪婪的量词使子串到达最右边的边界,而懒惰的量词将匹配第一次出现的子模式。

请参阅示例

> library(stringr)
> str_extract('xxx aaaab yyy', "a[^ab]*b")
[1] "ab"
> str_extract('xxx aaa xxx aaa zzz', "xxx.*?zzz")
[1] "xxx aaa xxx aaa zzz"
> str_extract('xxx aaa xxx aaa zzz', "xxx(?:(?!xxx|zzz).)*zzz")
[1] "xxx aaa zzz"

第一个和第三个场景返回最短的窗口,第二个是当前问题的说明,但带有多字符输入。

场景 1. 边界是单个字符

如果ab是单个字符,则通过使用否定字符类找到最短窗口。a[^ab]*b将轻松地从a直到下一个获取子字符串,b中间没有as 和bs。

场景 2. 边界不是单个字符

在这些可以进一步展开的情况下,您可以使用经过调和的贪婪令牌。xxx(?:(?!xxx|zzz).)*zzz模式匹配,然后是除 linebreak char 之外的xxx任何 0+ 字符,它不是 axxxzzzchar 序列的起始字符(如果紧靠右侧的子字符串与前瞻模式匹配,则匹配失败(?!xxx|zzz)负前瞻),然后一个zzz

这些匹配场景可以很容易地与 base R 一起regmatches使用(使用支持前瞻的 PCRE 正则表达式风格):

> x <- 'xxx aaa xxx aaa zzz xxx bbb xxx ccc zzz'
> unlist(regmatches(x, gregexpr("xxx(?:(?!xxx|zzz).)*zzz", x, perl = TRUE)))
[1] "xxx aaa zzz" "xxx ccc zzz"

注意:当在基础 R 中使用 PCRE 正则表达式或str_extract/中的 ICU 正则表达式时str_match.不匹配换行符,要启用该行为,您需要(?s)在模式开始处添加(内联 DOTALL 修饰符)。

于 2016-11-11T21:24:35.753 回答