2014-07-16 16 views
11

我正在寻找一种有效的方法来提取字符串中两个子串之间的所有匹配。例如。说我要提取包含字符串R:提取两个子串之间包含的所有子串的最快方法

start="strt" 

stop="stp" 
in string 
x="strt111stpblablastrt222stp" 

之间的所有子我想获得矢量

"111" "222" 

什么是最有效的方式R中做到这一点?也许使用正则表达式?或者有更好的方法吗?

回答

12

对于这样简单的事情,base R处理这很好。

您可以使用perl=T开启PCRE并使用lookaround断言。

x <- 'strt111stpblablastrt222stp' 
regmatches(x, gregexpr('(?<=strt).*?(?=stp)', x, perl=T))[[1]] 
# [1] "111" "222" 

说明

(?<=   # look behind to see if there is: 
    strt  # 'strt' 
)    # end of look-behind 
.*?   # any character except \n (0 or more times) 
(?=   # look ahead to see if there is: 
    stp   # 'stp' 
)    # end of look-ahead 

编辑:根据新的语法如下更新答案。

您也可以考虑使用stringi包。

library(stringi) 
x <- 'strt111stpblablastrt222stp' 
stri_extract_all_regex(x, '(?<=strt).*?(?=stp)')[[1]] 
# [1] "111" "222" 

而且rm_betweenqdapRegex包。

library(qdapRegex) 
x <- 'strt111stpblablastrt222stp' 
rm_between(x, 'strt', 'stp', extract=TRUE)[[1]] 
# [1] "111" "222" 
+0

许多thx--这对于非常好的解释来说是完美的和thx! –

+0

@TomWenseleers非常受欢迎。 – hwnd

+0

+1,为完成我会提到'strt \ K'可以代替'(?<= strt)'(没有错,只是另一个选项) – zx81

2

既然可以有几个开始/停止每个输入的字符串,我认为一个正则表达式将是最有效的解决方案:

(?<=strt)(?:(?!stp).)* 

strt之后匹配的一切,直到字符串或stp,以先到者为准结束第一。如果您想声明总是有stp,请在正则表达式的末尾添加(?=stp)。你甚至可以将这个正则表达式应用于矢量。

regmatches(subject, gregexpr("(?<=strt)(?:(?!stp).)*", subject, perl=TRUE)); 
5

您也可以考虑:

library(qdap) 
unname(genXtract(x, "strt", "stp")) 
#[1] "111" "222" 

速度比较

x1 <- rep(x,1e5) 
system.time(res1 <- regmatches(x1,gregexpr('(?<=strt).*?(?=stp)',x1,perl=T))) 
# user system elapsed 
# 2.187 0.000 2.015 

system.time(res2 <- regmatches(x1, gregexpr("(?<=strt)(?:(?!stp).)*", x1, perl=TRUE))) 
#user system elapsed 
# 1.902 0.000 1.780 

system.time(res3 <- str_extract_all(x1, perl('(?<=strt).*?(?=stp)'))) 
# user system elapsed 
# 6.990 0.000 6.636 

system.time(res4 <- genXtract(x1, "strt", "stp")) ##setNames(genXtract(...), NULL) is a bit slower 
# user system elapsed 
# 1.457 0.000 1.414 

names(res4) <- NULL 
identical(res1,res4) 
#[1] TRUE 
+0

Thx为额外的稍快选项 - 这很好! –

4

如果你在谈论中的R串速度只有一个包来做到这一点 - stringi

x <- "strt111stpblablastrt222stp" 
hwnd <- function(x1) regmatches(x1,gregexpr('(?<=strt).*?(?=stp)',x1,perl=T)) 
Tim <- function(x1) regmatches(x1, gregexpr("(?<=strt)(?:(?!stp).)*", x1, perl=TRUE)) 
stringr <- function(x1) str_extract_all(x1, perl('(?<=strt).*?(?=stp)')) 
akrun <- function(x1) genXtract(x1, "strt", "stp") 
stringi <- function(x1) stri_extract_all_regex(x1, perl('(?<=strt).*?(?=stp)')) 

require(microbenchmark) 
microbenchmark(stringi(x), hwnd(x), Tim(x), stringr(x)) 
Unit: microseconds 
     expr  min  lq median  uq  max neval 
stringi(x) 46.778 58.1030 64.017 67.3485 123.398 100 
    hwnd(x) 61.498 73.1095 79.084 85.5190 111.757 100 
    Tim(x) 60.243 74.6830 80.755 86.3370 102.678 100 
stringr(x) 236.081 261.9425 272.115 279.6750 440.036 100 

不幸的是,我无法测试@akrun解决方案,因为qdap软件包在安装过程中有一些错误。只有他的解决方案看起来像可以击败弦乐器的那种......

+6

我预计'genXtract'要慢得多(慢10-20倍)。它的构建灵活性和易用性。在很多情况下,研究人员的时间比计算时间更有价值。如果是这种情况'genXtract'是一个很好的选择。如果你在追赶速度,那么我和你一样,是'stringi'的忠实粉丝。 –

+5

我不仅仅是'stringi'粉丝 - 我是作者:) – bartektartanus

相关问题