【问题标题】:How to split a string from right-to-left, like Python's rsplit()?如何从右到左拆分字符串,如 Python 的 rsplit()?
【发布时间】:2013-12-08 14:55:54
【问题描述】:

假设一个向量:

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

我想从最右边得到一个新的向量,并且只拆分一次。预期结果是:

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

这就像 python 的 rsplit() 函数。我目前的想法是反转字符串,并在stringr 中拆分str_split()

有更好的解决方案吗?

更新
这是我返回 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)
}

【问题讨论】:

    标签: r split stringr stringi


    【解决方案1】:

    负前瞻:

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

    a(?!b) 表示要找到这样的 a,而 b 后面没有。在这种情况下,.*_ 意味着无论多远(.*)都不应该有更多的_

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

    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 这个函数使用_(?=[^_]*_[^_]*_[^_]*$)

    【讨论】:

    • 我对perl不熟悉,能不能稍微解释一下,如果要拆分两个或多个“_”应该怎么改?
    • 非常感谢@Julius!
    【解决方案2】:

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

    在这里,我使用regexec 来捕获您的子字符串的开始和结束位置,并使用regmatches 来重建它们:

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

    而且这个不太学术,但很容易理解:

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

    【讨论】:

      【解决方案3】:

      拆分之后再粘贴回去怎么样?

      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"  
      

      【讨论】:

        【解决方案4】:

        我也想过一种与阿里非常相似的方法

        > 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"  
        

        【讨论】:

          【解决方案5】:

          这正是您想要的,并且是最简单的方法:

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

          解释:

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

          【讨论】:

          • 顺便说一句,如果你经常这样做,定义一个自定义函数str_rsplit(...) &lt;- function(...) { ... }
          猜你喜欢
          • 1970-01-01
          • 2022-01-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-04-28
          相关资源
          最近更新 更多