【问题标题】:Applying a function to a backreference within gsub in R将函数应用于 R 中 gsub 内的反向引用
【发布时间】:2014-08-26 13:02:44
【问题描述】:

我是 R 新手,并且被似乎不起作用的反向引用所困扰。在:

gsub("\\((\\d+)\\)", f("\\1"), string)

它正确地抓取了括号之间的数字,但没有应用(正确定义,否则工作)函数 f 来替换数字 --> 它实际上是传递给 f 的字符串“\1”。

是我遗漏了什么还是只是 R 没有处理这个问题?如果是这样,知道我该如何做类似的事情,即“即时”将一个函数应用于我正在解析的文本中括号之间出现的(实际上很多)数字?

非常感谢您的帮助。

【问题讨论】:

  • 将数字提取到向量中,在该向量上应用函数,将结果提供给gsub
  • 谢谢!是的,使用 gregexpr/regmatches 提取到向量很容易,我一直在考虑这个问题——但是如何将它反馈给 gsub?
  • @JMD 欢迎来到 stackoverflow。当您发布时,发布最少的数据集也很有帮助。此链接提供有关格式化问题的信息:stackoverflow.com/help/how-to-ask

标签: r backreference


【解决方案1】:

R 没有通过gsub 将函数直接应用于匹配项的选项。您实际上必须提取匹配项,转换值,然后替换值。使用regmatches 函数,这相对容易。例如

x<-"(990283)M (31)O (29)M (6360)M"

f<-function(x) {
    v<-as.numeric(substr(x,2,nchar(x)-1))
    paste0(v+5,".1")
}

m <- gregexpr("\\(\\d+\\)", x)
regmatches(x, m) <- lapply(regmatches(x, m), f)
x
# [1] "990288.1M 36.1O 34.1M 6365.1M"

当然,你可以让f 做任何你喜欢的事情,只要确保它是矢量友好的。当然,您可以将其包装在您自己的函数中

gsubf <- function(pattern, x, f) {
    m <- gregexpr(pattern, x)
    regmatches(x, m) <- lapply(regmatches(x, m), f)
    x   
}
gsubf("\\(\\d+\\)", x, f)

请注意,在这些示例中,我们没有使用捕获组,我们只是抓取整个匹配项。有一些方法可以提取捕获组,但它们有点混乱。如果您想提供一个需要进行此类提取的示例,我可能会想出一些更奇特的东西。

【讨论】:

  • 工作正常!没想到regmatches &lt;-。非常感谢。抱歉耽搁了,但我花了一些时间才发现 ifelse() 是矢量友好的,而 if() else 不是... R 很酷,但有时确实有点太特殊了!
  • 使用捕获组的一种方法是在f 中使用pattern 提取它以供进一步使用v &lt;- sub(pattern, "\\1", x)
  • 使用捕获组的类似用例:stackoverflow.com/a/49344399/2371031
【解决方案2】:

要在支持正则表达式的替换函数中使用回调,您可以使用gsubfnstringr 函数。

在它们之间进行选择时,请注意stringr 是基于 ICU 正则表达式引擎和 gsubfn,您可以使用默认的 TCL(如果 R 安装具有 tcltk 功能,否则它是默认的 TRE)或 PCRE(如果您传递 perl=TRUE 参数)。

另外,请注意gsubfn 允许访问匹配对象中的所有捕获组,而str_replace_all 只允许操纵整个匹配。因此,对于str_replace_all,正则表达式应该类似于(?&lt;=\()\d+(?=\)),其中1+ 位仅当它们被() 包围时才匹配,将它们排除在匹配之外。

使用stringr,您可以使用str_replace_all

library(stringr)  
string <- "(990283)M (31)O (29)M (6360)M"
## Callback function to increment found number:
f <- function(x) { as.integer(x) + 1 }
str_replace_all(string, "(?<=\\()\\d+(?=\\))", function(m) f(m))
## => [1] "(990284)M (32)O (30)M (6361)M"

使用gsubfn,传递perl=TRUEbackref=0 以便能够使用环视并修改整个匹配:

gsubfn("(?<=\\()\\d+(?=\\))", ~ f(m), string, perl=TRUE, backref=0)
## => [1] "(990284)M (32)O (30)M (6361)M"

如果模式中有多个组,请移除 backref=0 并在回调函数声明中枚举组值参数:

gsubfn("(\\()(\\d+)(\\))", function(m,n,o) paste0(m,f(n),o), string, perl=TRUE)
        ^ 1 ^^  2 ^^ 3 ^           ^^^^^^^          ^^^^   

【讨论】:

  • 当使用引用反向引用/捕获组的函数时,str_replace_all 的工作方式似乎与 gsub 没有任何不同。
  • @BrianD 如果您不能使用带有str_replace_all 的可调用对象,那么您使用的是一些非常旧的 R/stringr 版本。请参阅online R demo 证明stringr::str_replace_all 的工作原理与答案中的说明相同。无法在回调中访问捕获组,但我并没有声称它在答案中。 OP 使用对整个机器的引用str_replace_all 做得很好。
【解决方案3】:

这是用于多个不同的替换。

text="foo(200) (300)bar (400)foo (500)bar (600)foo (700)bar"

f=function(x)
{
  return(as.numeric(x[[1]])+5)
}
a=strsplit(text,"\\(\\K\\d+",perl=T)[[1]]

b=f(str_extract_all(text,perl("\\(\\K\\d+")))

paste0(paste0(a[-length(a)],b,collapse=""),a[length(a)])  #final output
#[1] "foo(205) (305)bar (405)foo (505)bar (605)foo (705)bar"

【讨论】:

  • 谢谢,但不,我要做的是直接通过函数替换字符串中的数字。而来自 stringr 的 str_replace_all 也不起作用,可能是因为它基于 gsub 等。
  • 再次感谢,但当字符串中多次出现 \\d 时似乎不起作用,例如:(990283)M (31)O (29)M (6360)米
【解决方案4】:

这是一种通过稍微调整stringr::str_replace() 的方法,在替换参数中,只需使用 lambda 公式作为替换参数,并通过 ..1 而不是 ""\\1" 引用捕获的组,所以你的 gsub("\\((\\d+)\\)", f("\\1"), string)将变为str_replace2(string, "\\((\\d+)\\)", ~f(..1)),或者在这个简单的例子中只是str_replace2(string, "\\((\\d+)\\)", f)

str_replace2 <- function(string, pattern, replacement, type.convert = TRUE){
  if(inherits(replacement, "formula"))
    replacement <- rlang::as_function(replacement)
  if(is.function(replacement)){
    grps_mat <- stringr::str_match(string, pattern)[,-1, drop = FALSE]
    grps_list <- lapply(seq_len(ncol(grps_mat)), function(i) grps_mat[,i])
    if(type.convert) {
      grps_list <- type.convert(grps_list, as.is = TRUE) 
      replacement <- rlang::exec(replacement, !!! grps_list)
      replacement <- as.character(replacement)
    } else {
      replacement <- rlang::exec(replacement, !!! grps_list)
    }
  }
  stringr::str_replace(string, pattern, replacement)
}

str_replace2(
  "foo (4)",
  "\\((\\d+)\\)", 
  sqrt)
#> [1] "foo 2"

str_replace2(
  "foo (4) (5)",
  "\\((\\d+)\\) \\((\\d+)\\)", 
  ~ sprintf("(%s)", ..1 * ..2))
#> [1] "foo (20)"

reprex package (v0.3.0) 于 2020-01-24 创建

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-02
    • 1970-01-01
    • 2019-12-17
    • 2019-11-08
    • 2018-09-10
    • 2015-11-21
    • 2010-11-26
    • 1970-01-01
    相关资源
    最近更新 更多