【问题标题】:Non-greedy string regular expression matching非贪心字符串正则表达式匹配
【发布时间】:2013-05-16 01:03:54
【问题描述】:

我很确定我在这里遗漏了一些明显的东西,但我不能让 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.html 中的“贪婪”评论,我希望匹配只是ab

默认情况下重复是贪心的,所以使用最大可能的重复次数。 可以通过附加将其更改为“最小”?到量词。 (还有更多 允许近似匹配的量词:请参阅 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\">"

【问题讨论】:

    标签: regex r stringr


    【解决方案1】:

    概念比较难,所以我会尽力而为...如果有点混乱,请随时编辑和解释。

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

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

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

    这里所有的字符串aaaab yaaaab yyaaaab yyy都匹配了模式并且从相同的位置开始,但是因为非贪婪模式而返回了第一个。


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

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

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

    【讨论】:

    【解决方案2】:

    问题匹配两个字符串之间的最短窗口。 @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. 边界不是单个字符

    在这些可以进一步展开的情况下,您可以使用tempered greedy tokenxxx(?:(?!xxx|zzz).)*zzz 模式匹配 xxx,然后是除换行符之外的任何 0+ 字符,它不是 xxxzzz 字符序列的起始字符((?!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"
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-05
      • 1970-01-01
      • 2012-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多