R:提取两个子串之间包含的所有子串的最快方法

时间:2014-07-16 06:33:59

标签: regex r string substring

我正在寻找一种有效的方法来提取字符串中两个子串之间的所有匹配。例如。说我想提取字符串

之间包含的所有子串
start="strt"

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

我想获得矢量

"111" "222"

在R中执行此操作的最有效方法是什么?也许使用正则表达式?或者有更好的方法吗?

4 个答案:

答案 0 :(得分:13)

对于像这样简单的事情,基地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"

qdapRegex 包中的rm_between

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

答案 1 :(得分: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

答案 2 :(得分:5)

如果你在谈论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软件包在安装过程中有一些错误。只有他的解决方案看起来像能击败弦乐的那个......

答案 3 :(得分:2)

由于每个输入可以有多个启动/停止字符串,我认为正则表达式将是最有效的解决方案:

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

将匹配strt之后的所有内容,直到字符串结尾或stp,以先到者为准。如果您想声明始终存在stp,请在正则表达式的末尾添加(?=stp)。您甚至可以将此正则表达式应用于矢量。

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