将字符串拆分为固定大小的块

时间:2016-03-26 06:31:28

标签: r string

这似乎是一个非常简单的任务,但我在基础R中找不到一个好的解决方案。我有一个2N字符的字符串。如何将其拆分为长度为N的字符向量,每个元素为2个字符的字符串?

我可以使用substrVectorize之类的内容:

vss <- Vectorize(substr, c("start", "stop"))
ch <- paste(rep("a", 1e6), collapse="")
vss(ch, seq(1, nchar(ch), by=2), seq(2, nchar(ch), by=2))

但这对于长字符串来说真的很慢(O(N ^ 2)我相信)。

1 个答案:

答案 0 :(得分:2)

如果你想要速度,Rcpp总是一个不错的选择:

library(Rcpp);
cppFunction('
    List strsplitN(std::vector<std::string> v, int N ) {
        if (N < 1) throw std::invalid_argument("N must be >= 1.");
        List res(v.size());
        for (int i = 0; i < v.size(); ++i) {
            int num = v[i].size()/N + (v[i].size()%N == 0 ? 0 : 1);
            std::vector<std::string> resCur(num,std::string(N,0));
            for (int j = 0; j < num; ++j) resCur[j].assign(v[i].substr(j*N,N));
            res[i] = resCur;
        }
        return res;
    }
');

ch <- paste(rep('a',1e6),collapse='');
system.time({ res <- strsplitN(ch,2L); });
##    user  system elapsed
##   0.109   0.015   0.121
head(res[[1L]]); tail(res[[1L]]);
## [1] "aa" "aa" "aa" "aa" "aa" "aa"
## [1] "aa" "aa" "aa" "aa" "aa" "aa"
length(res[[1L]]);
## [1] 500000

有用的参考:http://gallery.rcpp.org/articles/strings_with_rcpp/

更多演示:

strsplitN(c('abcd','efgh'),2L);
## [[1]]
## [1] "ab" "cd"
##
## [[2]]
## [1] "ef" "gh"
##
strsplitN(c('abcd','efgh'),3L);
## [[1]]
## [1] "abc" "d"
##
## [[2]]
## [1] "efg" "h"
##
strsplitN(c('abcd','efgh'),1L);
## [[1]]
## [1] "a" "b" "c" "d"
##
## [[2]]
## [1] "e" "f" "g" "h"
##
strsplitN(c('abcd','efgh'),5L);
## [[1]]
## [1] "abcd"
##
## [[2]]
## [1] "efgh"
##
strsplitN(character(),5L);
## list()
strsplitN(c('abcd','efgh'),0L);
## Error: N must be >= 1.

上述实施有两个重要注意事项:

1:它无法正确处理NA。当Rcpp被迫提出'NA'时,它似乎会串行化为std::string。您可以使用包装器轻松地在Rland中解决此问题,该包装用真正的NA替换违规列表组件。

x <- c('a',NA); strsplitN(x,1L);
## [[1]]
## [1] "a"
##
## [[2]]
## [1] "N" "A"
##
x <- c('a',NA); ifelse(is.na(x),NA,strsplitN(x,1L));
## [[1]]
## [1] "a"
##
## [[2]]
## [1] NA
##

2:它无法正确处理多字节字符。这是一个更棘手的问题,需要重写核心函数实现才能使用支持Unicode的遍历。修复此问题也会导致显着的性能损失,因为在分配循环之前,您无法在一次镜头中预先分配每个向量。

strsplitN('aΩ',1L);
## [[1]]
## [1] "a"    "\xce" "\xa9"
##
strsplit('aΩ','');
## [[1]]
## [1] "a" "Ω"
##