如何使用stringr的replace_all()函数替换字符串

时间:2016-04-02 03:07:41

标签: regex r stringr

stringr包具有有用的str_replace()str_replace_all()功能。例如

mystring <- "one fish two fish red fish blue fish"

str_replace(mystring, "fish", "dog") # replaces the first occurrence
str_replace_all(mystring, "fish", "dog") # replaces all occurrences

真棒。但你怎么样?

  1. 替换第二次出现的“鱼”?
  2. 替换最后一次出现的“fish”?
  3. 替换第2次到最后一次出现的“fish”?

3 个答案:

答案 0 :(得分:2)

一个有用的答案很大程度上取决于字符串以及你对它的了解。使用正则表达式,一个选项是构建一个匹配整行的正则表达式,但是在不同的部分,所以你可以把你喜欢的部分放回去:

str_replace(mystring, '(^.*?fish.*?)(fish)(.*?fish.*)', '\\1dog\\3')
# [1] "one fish two dog red fish blue fish"

其中替换中的\\1\\3分别与捕获的第一个和第三个括号匹配。请注意懒惰(不合理)量词*?,这些非常重要,因此您不会过度匹配。

当然,你可以做同样的事情来匹配第三次或第四次:

str_replace(mystring, '(^.*?fish.*?fish.*?)(fish)(.*)', '\\1dog\\3')
# [1] "one fish two fish red dog blue fish"
str_replace(mystring, '(^.*?fish.*?fish.*?fish.*?)(fish)(.*?)', '\\1dog\\3')
# [1] "one fish two fish red fish blue dog"

但这并不是非常有效。您可以使用量词来重复,但它们使替换组的编号有点混乱:

str_replace(mystring, '^((.*?fish.*?){3})(fish)(.*?)', '\\1dog\\4')
# [1] "one fish two fish red fish blue dog"

但是如果你让重复的组不捕获(?: ... ),那就更有意义了:

str_replace(mystring, '^((?:.*?fish.*?){3})(fish)(.*?)', '\\1dog\\3')
# [1] "one fish two fish red fish blue dog"

但所有这些都是很多正则表达式。一个更简单的选择(取决于上下文和你喜欢的正则表达式,我想)可能是使用strsplit然后重新组合,collapse分开:

mystrlist <- strsplit(mystring, 'fish ')[[1]] # match the space so not the last "fish$"
paste0(c(mystrlist[1], 
         paste0(mystrlist[2:3], collapse = 'dog '), 
         mystrlist[4]), 
       collapse = 'fish ')
# [1] "one fish two dog red fish blue fish"

paste0(c(mystrlist[1:2], 
         paste0(mystrlist[3:4], collapse = 'dog ')), 
       collapse = 'fish ')
# [1] "one fish two fish red dog blue fish"

当然,对于最后一个单词,这并不是非常好用,但行尾正则表达式$使用str_replace(或只是sub)非常容易达到这个目的:

sub('fish$', 'dog', mystring)
# [1] "one fish two fish red fish blue dog"

底线:很大程度上取决于上下文最佳选择是什么,但遗憾的是没有额外的参数来替换哪个匹配。

答案 1 :(得分:2)

对于第一个和最后一个,我们可以使用stri_replace中的stringi,因为它有选项

 library(stringi)
 stri_replace(mystring, fixed="fish", "dog", mode="first")
 #[1] "one dog two fish red fish blue fish"

 stri_replace(mystring, fixed="fish", "dog", mode="last")
 #[1] "one fish two fish red fish blue dog"

mode只能包含'first','last'和'all'值。因此,其他选项不在默认功能中。我们可能必须使用regex选项来更改它。

使用sub,我们可以进行第n次替换

sub("^((?:(?!fish).)*fish(?:(?!fish).)*)fish", 
           "\\1dog", mystring, perl=TRUE)
#[1] "one fish two dog red fish blue fish"

或者我们可以使用

 sub('^((.*?fish.*?){2})fish', "\\1\\dog", mystring, perl=TRUE)
 #[1] "one fish two fish red dog blue fish"

为了方便起见,我们可以创建一个功能来执行此操作

patfn <- function(n){
 stopifnot(n>1)
 sprintf("^((.*?\\bfish\\b.*?){%d})\\bfish\\b", n-1)
} 

并替换第n个'fish'除了第一个'fish',可以使用substr_replace

中的默认选项轻松完成
sub(patfn(2), "\\1dog", mystring, perl=TRUE)
#[1] "one fish two dog red fish blue fish"
sub(patfn(3), "\\1dog", mystring, perl=TRUE)
#[1] "one fish two fish red dog blue fish"
sub(patfn(4), "\\1dog", mystring, perl=TRUE)
#[1] "one fish two fish red fish blue dog"

这也适用于str_replace

 str_replace(mystring, patfn(2), "\\1dog")
 #[1] "one fish two dog red fish blue fish"
 str_replace(mystring, patfn(3), "\\1dog")
 #[1] "one fish two fish red dog blue fish"

根据上面提到的模式/替换,我们可以创建一个新功能来完成大部分选项

replacerFn <- function(String, word, rword, n){
 stopifnot(n >0)
  pat <- sprintf(paste0("^((.*?\\b", word, "\\b.*?){%d})\\b",
           word,"\\b"), n-1)
  rpat <- paste0("\\1", rword)
  if(n >1) { 
    stringr::str_replace(String, pat, rpat)
   } else {
    stringr::str_replace(String, word, rword)
    }
 }


 replacerFn(mystring, "fish", "dog", 1)
 #[1] "one dog two fish red fish blue fish"
 replacerFn(mystring, "fish", "dog", 2)
 #[1] "one fish two dog red fish blue fish"
 replacerFn(mystring, "fish", "dog", 3)
 #[1] "one fish two fish red dog blue fish"
 replacerFn(mystring, "fish", "dog", 4)
 #[1] "one fish two fish red fish blue dog"

答案 2 :(得分:0)

stringr设计用于处理字符向量。它没有允许在矢量元素内进行任何详细级别播放的功能。但是一种简单的方法是将字符串拆分为子集的字符向量,在该向量上应用stringr函数(因为stringr确实很擅长),然后将向量重新合并为一个串。当然,这些步骤可以转换为功能。

只要需要在单个字符串中执行某些操作,就可以应用此方法。

对于此处提供的示例,合适的子集是单个单词。

因此,要替换字符串的第n个元素:

library(stringr)

replace_function <- function(string, word, rword, n) {
  vec <- unlist(strsplit(string, " "))
  vec[str_which(vec, word)[n]] <- rword
  str_c(vec, collapse = " ")
}

replace_function(mystring, "fish", "dog", 1)
[1] "one dog two fish red fish blue fish"

replace_function(mystring, "fish", "dog", 2)
[1] "one fish two dog red fish blue fish"

要添加最后一个元素中的第n个元素,只需添加rev()

replace_end_function <- function(string, word, rword, n) {
  vec <- unlist(strsplit(string, " "))
  vec[rev(str_which(vec, word))[n]] <- rword
  str_c(vec, collapse = " ")
}

replace_end_function(mystring, "fish", "dog", 1)
[1] "one fish two fish red fish blue dog"

replace_end_function(mystring, "fish", "dog", 2)
[1] "one fish two fish red dog blue fish"

并将第n个元素替换为最后一个元素:

replace_end_function <- function(string, word, rword, n) {
  vec <- unlist(strsplit(string, " "))
  vec[str_which(vec, word)[n:length(str_which(vec, word))]] <- rword
  str_c(vec, collapse = " ")
}

replace_end_function(mystring, "fish", "dog", 1)
[1] "one dog two dog red dog blue dog"

replace_end_function(mystring, "fish", "dog", 2)
[1] "one fish two dog red dog blue dog"

replace_end_function(mystring, "fish", "dog", 3)
[1] "one fish two fish red dog blue dog"

replace_end_function(mystring, "fish", "dog", 4)
[1] "one fish two fish red fish blue dog"

请注意,此答案不使用OP所要求的str_replace(),因为正如OP所指出的,str_replace()仅适用于向量的第一个元素,而str_replace_all()则适用在所有这些上。因此,它们不是stringr包中最合适的函数来回答这个问题:用str_which()的结果进行索引更合适(当然,一旦将单个字符串拆分为字符串向量, )。