【问题标题】:Insert a character at a specific location in a string在字符串的特定位置插入字符
【发布时间】:2012-12-01 13:32:43
【问题描述】:

我想在字符串的特定位置插入一个额外的字符(或新字符串)。比如我想在abcefg的第四个位置插入d得到abcdefg

现在我正在使用:

old <- "abcefg"
n <- 4
paste(substr(old, 1, n-1), "d", substr(old, n, nchar(old)), sep = "")

我可以为这个任务编写一个简单的函数,但我只是好奇是否有一个现有的函数。

【问题讨论】:

    标签: string r


    【解决方案1】:

    您可以使用正则表达式和gsub 来做到这一点。

    gsub('^([a-z]{3})([a-z]+)$', '\\1d\\2', old)
    # [1] "abcdefg"
    

    如果您想动态执行此操作,可以使用paste 创建表达式:

    letter <- 'd'
    lhs <- paste0('^([a-z]{', n-1, '})([a-z]+)$')
    rhs <- paste0('\\1', letter, '\\2')
    gsub(lhs, rhs, old)
    # [1] "abcdefg"
    

    根据 DWin 的评论,您可能希望这更笼统。

    gsub('^(.{3})(.*)$', '\\1d\\2', old)
    

    这样任何三个字符都将匹配,而不仅仅是小写。 DWin 还建议使用sub 而不是gsub。这样您就不必担心^,因为sub 只会匹配第一个实例。但我喜欢在正则表达式中明确表达,并且只在我理解它们并发现需要更通用性时才转向更一般的表达式。


    正如 Greg Snow 所说,您可以使用另一种形式的正则表达式来查找匹配项:

    sub( '(?<=.{3})', 'd', old, perl=TRUE )
    

    并且还可以使用sprintf 而不是paste0 在上面构建我的动态gsub

    lhs <- sprintf('^([a-z]{%d})([a-z]+)$', n-1) 
    

    或者他的sub正则表达式:

    lhs <- sprintf('(?<=.{%d})',n-1)
    

    【讨论】:

    • 我喜欢正则表达式解决方案,但会使用sub(),我建议使用更通用的模式:^(.{3})(.*$)"。目前任何非小写字母都会破坏替换。
    • 我喜欢尽可能具体地使用正则表达式,这样它们就会失败而不是意外成功。但你说得很好!
    • @Justin 感谢您指出gsub 函数。我知道这些模式匹配功能非常强大,但实际上并不知道如何使用它们。它们似乎非常复杂。我需要在他们身上搜索更多内容。
    • 您可以使用后视匹配来获得正确的位置而不捕获:sub( '(?&lt;=.{3})', 'd', old, perl=TRUE )gsub( '(?&lt;=^.{3})', 'd', old, perl=TRUE )。哪个更简单可能是个人喜好问题。
    • 另外,我认为这是在动态情况下sprintf 可能比paste0 更简单的情况之一:lhs &lt;- sprintf('^([a-z]{%d})([a-z]+)$', n-1)lhs &lt;- sprintf('(?&lt;=.{%d})',n-1)
    【解决方案2】:

    stringi 再次为救援包!提供的解决方案中最简单优雅的解决方案。

    stri_sub 函数允许您提取字符串的一部分并替换它的一部分,如下所示:

    x <- "abcde"
    stri_sub(x, 1, 3) # from first to third character
    # [1] "abc"
    stri_sub(x, 1, 3) <- 1 # substitute from first to third character
    x
    # [1] "1de"
    

    但如果你这样做:

    x <- "abcde"
    stri_sub(x, 3, 2) # from 3 to 2 so... zero ?
    # [1] ""
    stri_sub(x, 3, 2) <- 1 # substitute from 3 to 2 ... hmm
    x
    # [1] "ab1cde"
    

    然后不会删除任何字符,但会插入新字符。这不是很酷吗? :)

    【讨论】:

    • 你知道如何用这种优雅的方法在多个位置插入吗?
    • 这里我天真地尝试使用reduce(),但看起来很笨拙:stackoverflow.com/questions/47336114
    • 解决困扰我一段时间的问题的出色方法
    • 有趣的函数,但 stri_sub &lt;- 不容易放入管道,而 sub 函数是。 (仅当您想要管道时才重要......)
    • @Bastien & @bartektartanus 你可能已经意识到这一点:它现在可以通过stringi::stri_sub_replace(x, 3,2, value = 1)进行管道传输了
    【解决方案3】:

    @Justin 的回答是我实际采用的方法,因为它具有灵活性,但 this 也可能是一种有趣的方法。

    您可以将字符串视为“固定宽度格式”并指定要插入字符的位置:

    paste(read.fwf(textConnection(old), 
                   c(4, nchar(old)), as.is = TRUE), 
          collapse = "d")
    

    使用sapply 时的输出特别好,因为您可以将原始字符串视为“名称”。

    newold <- c("some", "random", "words", "strung", "together")
    sapply(newold, function(x) paste(read.fwf(textConnection(x), 
                                              c(4, nchar(x)), as.is = TRUE), 
                                     collapse = "-WEE-"))
    #            some          random           words          strung        together 
    #   "some-WEE-NA"   "rand-WEE-om"    "word-WEE-s"   "stru-WEE-ng" "toge-WEE-ther" 
    

    【讨论】:

      【解决方案4】:

      您的原始方法(即在索引处拆分字符串并粘贴到插入的文本中)可以变成这样的通用函数:

      split_str_by_index <- function(target, index) {
        index <- sort(index)
        substr(rep(target, length(index) + 1),
               start = c(1, index),
               stop = c(index -1, nchar(target)))
      }
      
      #Taken from https://stat.ethz.ch/pipermail/r-help/2006-March/101023.html
      interleave <- function(v1,v2)
      {
        ord1 <- 2*(1:length(v1))-1
        ord2 <- 2*(1:length(v2))
        c(v1,v2)[order(c(ord1,ord2))]
      }
      
      insert_str <- function(target, insert, index) {
        insert <- insert[order(index)]
        index <- sort(index)
        paste(interleave(split_str_by_index(target, index), insert), collapse="")
      }
      

      示例用法:

      > insert_str("1234567890", c("a", "b", "c"), c(5, 9, 3))
      [1] "12c34a5678b90"
      

      这允许您在索引向量给定的位置插入字符向量。 split_str_by_indexinterleave 函数本身也很有用。

      编辑:

      我修改了代码以允许任何顺序的索引。以前,索引需要按升序排列。

      【讨论】:

        【解决方案5】:

        我创建了一个名为substr1 的自定义函数来处理字符串中字符的提取、替换和插入。在每个会话开始时运行这些代码。随意尝试一下,如果需要改进,请告诉我。

        # extraction
        substr1 <- function(x,y) {
          z <- sapply(strsplit(as.character(x),''),function(w) paste(na.omit(w[y]),collapse=''))
          dim(z) <- dim(x)
          return(z) }
        
        # substitution + insertion
        `substr1<-` <- function(x,y,value) {
          names(y) <- c(value,rep('',length(y)-length(value)))
          z <- sapply(strsplit(as.character(x),''),function(w) {
            v <- seq(w)
            names(v) <- w
            paste(names(sort(c(y,v[setdiff(v,y)]))),collapse='') })
          dim(z) <- dim(x)
          return(z) }
        
        # demonstration
        abc <- 'abc'
        substr1(abc,1)
        # "a"
        substr1(abc,c(1,3))
        # "ac"
        substr1(abc,-1)
        # "bc"
        substr1(abc,1) <- 'A'
        # "Abc"
        substr1(abc,1.5) <- 'A'
        # "aAbc"
        substr1(abc,c(0.5,2,3)) <- c('A','B')
        # "AaB"
        

        【讨论】:

          【解决方案6】:

          我花了一些时间来理解正则表达式,然后我找到了自己的数字

          最终结果是

          old <- "89580000"
          gsub('^([0-9]{5})([0-9]+)$', '\\1-\\2', old)
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-09-25
            • 1970-01-01
            • 1970-01-01
            • 2020-01-20
            • 1970-01-01
            • 2018-03-15
            相关资源
            最近更新 更多