R: быстрый способ извлечь все подстроки, содержащиеся между двумя подстроками
Я ищу эффективный способ извлечь все совпадения между двумя подстроками в символьной строке. Например. Я хочу извлечь все подстроки, содержащиеся между строкой
start="strt"
и
stop="stp"
in string
x="strt111stpblablastrt222stp"
Я хотел бы получить вектор
"111" "222"
Каков наиболее эффективный способ сделать это в R? Возможно, с помощью регулярного выражения? Или есть лучшие способы?
Ответы
Ответ 1
Для чего-то такого простого база R отлично справляется с этим.
Вы можете включить PCRE с помощью perl=T
и использовать 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
EDIT: Обновлено ниже в соответствии с новым синтаксисом.
Вы также можете использовать пакет stringi.
library(stringi)
x <- 'strt111stpblablastrt222stp'
stri_extract_all_regex(x, '(?<=strt).*?(?=stp)')[[1]]
# [1] "111" "222"
И rm_between
из пакета qdapRegex.
library(qdapRegex)
x <- 'strt111stpblablastrt222stp'
rm_between(x, 'strt', 'stp', extract=TRUE)[[1]]
# [1] "111" "222"
Ответ 2
Вы также можете рассмотреть:
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
Ответ 3
Если вы говорите о скорости в 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 есть некоторые ошибки во время установки. И только его решение выглядит как тот, который может бить струн...
Ответ 4
Поскольку на вход может быть несколько строк начала/остановки, я думаю, что наиболее эффективным решением будет регулярное выражение:
(?<=strt)(?:(?!stp).)*
будет соответствовать всем значениям после strt
до конца строки или stp
, в зависимости от того, что наступит раньше. Если вы хотите утверждать, что всегда есть stp
, добавьте (?=stp)
в конец регулярного выражения. Вы можете применить это регулярное выражение к вектору.
regmatches(subject, gregexpr("(?<=strt)(?:(?!stp).)*", subject, perl=TRUE));