【问题标题】:(How) can multiple backreference be used in alternation patterns?(如何)可以在交替模式中使用多个反向引用?
【发布时间】:2021-12-30 09:55:28
【问题描述】:

这个问题是从那个问题Function to count of consecutive digits in a string vector 衍生出来的。

假设我有 x 之类的字符串:

x <- c("555123", "57333", "21112", "12345", "22144", "44440")

并且想要检测那些在25 之间的任何数字在立即重复中出现的次数与其自身一样多的字符串。也就是说,如果字符串包含22333444455555,则匹配。

如果我使用反向引用以小块的方式处理此任务,一切都很好:

str_detect(x, "(2)\\1{1}")
[1] FALSE FALSE FALSE FALSE  **TRUE** FALSE

str_detect(x, "(3)\\1{2}")
[1] FALSE  **TRUE** FALSE FALSE FALSE FALSE

str_detect(x, "(4)\\1{3}")
[1] FALSE FALSE FALSE FALSE FALSE  **TRUE**

但是,如果我使用具有允许数字的向量为所有匹配项寻求单一解决方案

digits <- 2:5

还有一个交替模式,例如:

patt <- paste0("(", digits, ")\\1{", digits - 1, "}", collapse = "|")
patt
[1] "(2)\\1{1}|(3)\\1{2}|(4)\\1{3}|(5)\\1{4}"

并在str_detect中输入patt,这只会检测到第一个替代方案,即(2)\\1{1}

str_detect(x, patt)
[1] FALSE FALSE FALSE FALSE  **TRUE** FALSE 

是不能在交替模式中使用的反向引用吗?如果是这样,那么为什么 for 循环遍历每个选项单独也不起作用?

res <- c()
for(i in 2:5){
  res <- str_detect(x, paste0("(", i, ")\\1{", i - 1, "}"))
}
res
[1] FALSE FALSE FALSE FALSE FALSE FALSE

非常感谢您就此事提出建议!

【问题讨论】:

  • 我认为反向引用也应该增加,因为你有更多的组(2)\1|(3)\2{2}|(4)\3{3}|(5)\4{4}
  • 是的,太棒了!此模式有效:paste0("(", digits, ")\\", c(1, digits), "{", digits - 1, "}", collapse = "|")。请作为答案发布,以便我接受。
  • 我已经发布了一个关于正则表达式的答案。 R 解决方案可能会发布更好的答案,因为我对 R 的了解最少。

标签: r regex


【解决方案1】:

在您的模式(2)\\1{1}|(3)\\1{2}|(4)\\1{3}|(5)\\1{4} 中,量词重复将反向引用匹配到第一个捕获组。这就是为什么你只匹配第一个选项。

您可以重复下一个捕获组,因为有多个组。

(2)\\1{1}|(3)\\2{2}|(4)\\3{3}|(5)\\4{4}

(2)\\1{1} 可以只是 (2)\\1,但这没关系,因为您可以动态组合模式

【讨论】:

    【解决方案2】:

    这个呢?

    > grepl(
    +   paste0(sapply(2:5, function(i) sprintf("(%s)\\%s{%s}", i, i - 1, i - 1)), collapse = "|"),
    +   x
    + )
    [1] FALSE  TRUE FALSE FALSE  TRUE  TRUE
    

    > rowSums(sapply(2:5, function(i) grepl(sprintf("(%s)\\1{%s}", i, i - 1), x))) > 0
    [1] FALSE  TRUE FALSE FALSE  TRUE  TRUE
    

    【讨论】:

    • 不错。也许你可以说一两句我不熟悉的sprintf
    • @ChrisRuehlemann sprintf 打印具有给定格式的字符串,其中%s 是指您要填充到格式中的值。您可以尝试?sprintf 了解有关此功能的更多信息。
    【解决方案3】:

    如 cmets 中所述,您需要更新正则表达式:

    patt = paste0(
      "(", digits, ")\\", digits - 1, "{", digits - 1, "}", 
      collapse = "|"
    )
    str_detect(x, patt)
    

    输出:

    [1] FALSE  TRUE FALSE FALSE  TRUE  TRUE
    

    在您的for 循环中,您每次都在替换res,因此当您最后打印res 时,您会看到i 为5 时的结果。如果您改用print()

    for(i in 2:5){
      print(str_detect(x, paste0("(", i, ")\\1{", i - 1, "}")))
    }
    

    输出:

    [1] FALSE FALSE FALSE FALSE  TRUE FALSE
    [1] FALSE  TRUE FALSE FALSE FALSE FALSE
    [1] FALSE FALSE FALSE FALSE FALSE  TRUE
    [1] FALSE FALSE FALSE FALSE FALSE FALSE
    

    如果你想使用循环:

    map_lgl(x, function(str) {
      any(map_lgl(
        2:5, 
        ~ str_detect(str, paste0("(", .x, ")\\1{", .x - 1, "}"))
      ))
    })
    

    输出:

    [1] FALSE  TRUE FALSE FALSE  TRUE  TRUE
    

    【讨论】:

    • 一个绝妙的综合答案。我没有接受它,因为在您的帖子之前的评论中提到了这个技巧。
    猜你喜欢
    • 1970-01-01
    • 2019-03-25
    • 2012-10-30
    • 1970-01-01
    • 1970-01-01
    • 2018-08-19
    • 1970-01-01
    • 2017-03-27
    • 1970-01-01
    相关资源
    最近更新 更多