如何将一个字符串分割成指定长度的子字符串?

39
我有一个字符串,如下所示:

"aabbccccdd"

我希望将该字符串分成长度为2的子字符串向量:

"aa" "bb" "cc" "cc" "dd"

5个回答

61

以下是一种方法

substring("aabbccccdd", seq(1, 9, 2), seq(2, 10, 2))
#[1] "aa" "bb" "cc" "cc" "dd"

或更普遍地说

text <- "aabbccccdd"
substring(text, seq(1, nchar(text)-1, 2), seq(2, nchar(text), 2))
#[1] "aa" "bb" "cc" "cc" "dd"

编辑:这样做会快得多

sst <- strsplit(text, "")[[1]]
out <- paste0(sst[c(TRUE, FALSE)], sst[c(FALSE, TRUE)])

它首先将字符串拆分为字符。然后,它将偶数元素和奇数元素粘合在一起。

时间

text <- paste(rep(paste0(letters, letters), 1000), collapse="")
g1 <- function(text) {
    substring(text, seq(1, nchar(text)-1, 2), seq(2, nchar(text), 2))
}
g2 <- function(text) {
    sst <- strsplit(text, "")[[1]]
    paste0(sst[c(TRUE, FALSE)], sst[c(FALSE, TRUE)])
}
identical(g1(text), g2(text))
#[1] TRUE
library(rbenchmark)
benchmark(g1=g1(text), g2=g2(text))
#  test replications elapsed relative user.self sys.self user.child sys.child
#1   g1          100  95.451 79.87531    95.438        0          0         0
#2   g2          100   1.195  1.00000     1.196        0          0         0

2
太棒了!第二个版本真的非常非常快! - MadSeb
我想知道是否有类似的东西,可以将 "aabbbcccccdd" 分割成 aa bbb ccccc dd。目前我使用 grepexpr。 - jackStinger
有没有什么技巧可以将快速版本扩展到任意块长度“n”? - mathematical.coffee
@mathematical.coffee 或许可以这样做:do.call(paste0, lapply(seq_len(n), function(i) { idx <- rep(FALSE, n); idx[i] <- TRUE; sst[idx] })) 但请参考我的评论关于注意你的输入是否能被 n 整除。 - GSee
请仔细核对结果:测试 复制 耗时 相对值 用户时间 系统时间 用户子进程 系统子进程 g1 100 0.262 1.000 0.216 0.044 0 0 g2 100 0.562 2.145 0.530 0.031 0 0 - vwvan
显示剩余2条评论

19

这里有两种简单的可能性:

s <- "aabbccccdd"
  1. gregexpr and regmatches:

    regmatches(s, gregexpr(".{2}", s))[[1]]
    # [1] "aa" "bb" "cc" "cc" "dd"
    
  2. strsplit:

    strsplit(s, "(?<=.{2})", perl = TRUE)[[1]]
    # [1] "aa" "bb" "cc" "cc" "dd"
    

1
这些选项对于提议的s来说是等效的,但如果s <- "aabbccccdde"呢?我更喜欢第二个选项。 - rjss
1
第二个选项适用于任何数字,例如 strsplit(s, "(?<=.{11})", perl = TRUE)[[1]],而第一个选项仅适用于单个数字。 - Øystein S

12
string <- "aabbccccdd"
# total length of string
num.chars <- nchar(string)

# the indices where each substr will start
starts <- seq(1,num.chars, by=2)

# chop it up
sapply(starts, function(ii) {
  substr(string, ii, ii+1)
})

这给出了什么

[1] "aa" "bb" "cc" "cc" "dd"

2

我们可以使用矩阵来对字符进行分组:

s2 <- function(x) {
  m <- matrix(strsplit(x, '')[[1]], nrow=2)
  apply(m, 2, paste, collapse='')
}

s2('aabbccddeeff')
## [1] "aa" "bb" "cc" "dd" "ee" "ff"

不幸的是,对于奇数长度的输入,这会出现错误提示:

s2('abc')
## [1] "ab" "ca"
## Warning message:
## In matrix(strsplit(x, "")[[1]], nrow = 2) :
##   data length [3] is not a sub-multiple or multiple of the number of rows [2]

更不幸的是,从@GSee传回的g1g2对于奇数长度的输入默默地返回不正确的结果:
g1('abc')
## [1] "ab"

g2('abc')
## [1] "ab" "cb"

这是一种类似于s2的函数,它接受一个参数来指定每组字符的数量,并在必要时将最后一个条目留短:

s <- function(x, n) {
  sst <- strsplit(x, '')[[1]]
  m <- matrix('', nrow=n, ncol=(length(sst)+n-1)%/%n)
  m[seq_along(sst)] <- sst
  apply(m, 2, paste, collapse='')
}

s('hello world', 2)
## [1] "he" "ll" "o " "wo" "rl" "d" 
s('hello world', 3)
## [1] "hel" "lo " "wor" "ld" 

(它确实比 g2 慢,但比 g1 快大约7倍)


如果字符数量可能是奇数,那么我认为在之后处理会比引入一个apply循环更快。我敢打赌这个更快:out <- g2(x); if (nchar(x) %% 2 == 1L) out[length(out)] <- substring(out[length(out)], 1, 1); out - GSee

1
丑陋但有效
sequenceString <- "ATGAATAAAG"

J=3#maximum sequence length in file
sequenceSmallVecStart <-
  substring(sequenceString, seq(1, nchar(sequenceString)-J+1, J), 
    seq(J,nchar(sequenceString), J))
sequenceSmallVecEnd <-
    substring(sequenceString, max(seq(J, nchar(sequenceString), J))+1)
sequenceSmallVec <-
    c(sequenceSmallVecStart,sequenceSmallVecEnd)
cat(sequenceSmallVec,sep = "\n")

给出 ATG AAT AAA G


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接