【问题标题】:R: fastest way to extract all substrings contained between two substringsR:提取两个子字符串之间包含的所有子字符串的最快方法
【发布时间】:2014-09-06 13:34:27
【问题描述】:

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

start="strt"

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

我想获取矢量

"111" "222"

在 R 中最有效的方法是什么?也许使用正则表达式?还是有更好的方法?

【问题讨论】:

    标签: regex r string substring


    【解决方案1】:

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

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

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

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

    【讨论】:

      【解决方案2】:

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

      还有来自 qdapRegex 包的 rm_between

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

      【讨论】:

      • 很多谢谢 - 这是完美的,谢谢你的解释!
      • +1,为了完成,我会提到strt\K 可以替换(?&lt;=strt)(没有错,只是另一种选择)
      【解决方案3】:

      您也可以考虑:

      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
      

      【讨论】:

      • 感谢额外的稍微快一点的选项 - 太好了!!
      【解决方案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 包在安装过程中出现了一些错误。而且只有他的解决方案看起来可以击败 stringi...

      【讨论】:

      • 我预计 genXtract 会慢得多(慢 10-20 倍)。它专为灵活性和易用性而设计。在许多情况下,研究人员的时间比计算时间更有价值。如果是这种情况,genXtract 是一个很好的选择。如果您追求速度,那么我和您一样是stringi 的忠实粉丝。
      • 我不仅仅是 stringi 粉丝 - 我还是作者 :)
      • perl is deprecated, use regex instead,我不想编辑答案,因为此后基准测试结果可能已经改变。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-31
      • 2013-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多