如何从右到左分割字符串,如Python的rsplit()?

时间:2013-12-08 14:55:54

标签: r split stringr stringi

假设一个向量:

xx.1 <- c("zz_ZZ_uu_d", "II_OO_d")

我希望从最右边分割出一个新的向量,然后只拆分一次。预期结果将是:

c("zz_ZZ_uu", "d", "II_OO", "d").

这就像python的rsplit()功能。我目前的想法是反转字符串,并将str_split()stringr分开。

有更好的解决方案吗?

更新
这是我的解决方案返回n分裂,取决于stringr和stringi。有人提供带有基本功能的版本会很好。

rsplit <- function (x, s, n) {
  cc1 <- unlist(stringr::str_split(stringi::stri_reverse(x), s, n))
  cc2 <- rev(purrr::map_chr(cc1, stringi::stri_reverse))
  return(cc2)
}

5 个答案:

答案 0 :(得分:6)

否定前瞻:

unlist(strsplit(xx.1, "_(?!.*_)", perl = TRUE))
# [1] "zz_ZZ_uu" "d"        "II_OO"    "d"     

a(?!b)在哪里找到a之后没有b的{​​{1}}。在这种情况下,.*_表示无论多远(.*),都不应再有_

然而,概括这个想法似乎并不容易。首先,请注意,可以使用_(?=[^_]*$)将其重写为正向前瞻(查找_后跟除_之外的任何内容,此处$表示字符串的结尾)。那么一个不太优雅的概括就是

rsplit <- function(x, s, n) {
  p <- paste0("[^", s, "]*")
  rx <- paste0(s, "(?=", paste(rep(paste0(p, s), n - 1), collapse = ""), p, "$)")
  unlist(strsplit(x, rx, perl = TRUE))
}

rsplit(vec, "_", 1)
# [1] "a_b_c_d_e_f" "g"           "a"           "b"          
rsplit(vec, "_", 3)
# [1] "a_b_c_d" "e_f_g"   "a_b"    

例如如果n=3此函数使用_(?=[^_]*_[^_]*_[^_]*$)

答案 1 :(得分:2)

另外两个。在两者中,我使用"(.*)_(.*)"作为模式来捕获字符串的两个部分。请记住,*是贪婪的,因此第一个(.*)将匹配尽可能多的字符。

在这里,我使用regexec来捕捉您的断言开始和结束的位置,并regmatches重新构建它们:

unlist(lapply(regmatches(xx.1, regexec("(.*)_(.*)", xx.1)),
              tail, -1))

这一点不那么学术但很容易理解:

unlist(strsplit(sub("(.*)_(.*)", "\\1@@@\\2", xx.1), "@@@"))

答案 2 :(得分:1)

分裂之后将它粘贴在一起怎么样?

rsplit <- function( x, s ) {
  spl <- strsplit( "zz_ZZ_uu_d", s, fixed=TRUE )[[1]]
  res <- paste( spl[-length(spl)], collapse=s, sep="" )
  c( res, spl[length(spl)]  )
}
> rsplit("zz_ZZ_uu_d", "_")
[1] "zz_ZZ_uu" "d"  

答案 3 :(得分:1)

我也想过与Ari非常类似的方法

> res <- lapply(strsplit(xx.1, "_"), function(x){
    c(paste0(x[-length(x)], collapse="_" ), x[length(x)])
  }) 

> unlist(res)
[1] "zz_ZZ_uu" "d"        "II_OO"    "d"  

答案 4 :(得分:0)

这完全符合您的要求,是最简单的方法:

require(stringr)
as.vector(t(str_match(xx.1, '(.*)_(.*)') [,-1]))
[1] "zz_ZZ_uu" "d"        "II_OO"    "d"

说明:

  • str_split()不是你正在寻找的机器人,因为它只是从左到右分裂,然后分裂然后重新播放所有(n-1)最左边的匹配是完全浪费时间。因此,将str_split()与带有两个捕获组的正则表达式一起使用。请注意,第一个(.*)_将贪婪地匹配最后一次出现_的所有内容,这就是您想要的。 (如果至少有一个_并且返回NAs,则会失败)
  • str_match()返回一个矩阵,其中第一列是整个字符串,后续列是各个捕获组。我们不想要第一列,因此请使用[,-1]
  • 删除它
  • as.vector()将按列显示该矩阵,这不是您想要的,因此我们使用t()将其转置为逐行展开
  • str_match(string, pattern)在字符串和模式上进行矢量化,这是整洁的