Групповой захват в R с несколькими группами захвата
В R, возможно ли извлечь групповой захват из регулярного выражения? Насколько я могу судить, ни один из grep
, grepl
, regexpr
, gregexpr
, sub
или gsub
не возвращает группы.
Мне нужно извлечь пары ключ-значение из строк, которые закодированы таким образом:
\((.*?) :: (0\.[0-9]+)\)
Я всегда могу просто выполнять множественные grep с полным совпадением или выполнять некоторую внешнюю (не-R) обработку, но я надеялся, что смогу сделать все это внутри R. Есть ли функция или пакет, который предоставляет такую функцию сделать это?
Ответы
Ответ 1
str_match()
, из пакета stringr
, это сделает. Он возвращает матрицу символов с одним столбцом для каждой группы в матче (и один для всего матча):
> s = c("(sometext :: 0.1231313213)", "(moretext :: 0.111222)")
> str_match(s, "\\((.*?) :: (0\\.[0-9]+)\\)")
[,1] [,2] [,3]
[1,] "(sometext :: 0.1231313213)" "sometext" "0.1231313213"
[2,] "(moretext :: 0.111222)" "moretext" "0.111222"
Ответ 2
gsub делает это из вашего примера:
gsub("\\((.*?) :: (0\\.[0-9]+)\\)","\\1 \\2", "(sometext :: 0.1231313213)")
[1] "sometext 0.1231313213"
вам нужно удвоить escape\s в кавычках, затем они будут работать для регулярного выражения.
Надеюсь, что это поможет.
Ответ 3
Попробуйте regmatches()
и regexec()
:
regmatches("(sometext :: 0.1231313213)",regexec("\\((.*?) :: (0\\.[0-9]+)\\)","(sometext :: 0.1231313213)"))
[[1]]
[1] "(sometext :: 0.1231313213)" "sometext" "0.1231313213"
Ответ 4
gsub() может сделать это и вернуть только группу захвата:
Однако, чтобы это работало, вы должны явно выбирать элементы вне вашей группы захвата, как указано в справке gsub().
(...) элементы символьных векторов "x", которые не заменены, будут возвращены без изменений.
Итак, если ваш текст будет выбран в середине некоторой строки, добавление. * до и после группы захвата должно позволить вам только вернуть ее.
gsub(".*\\((.*?) :: (0\\.[0-9]+)\\).*","\\1 \\2", "(sometext :: 0.1231313213)")
[1] "sometext 0.1231313213"
Ответ 5
Мне нравятся регулярные выражения, совместимые с perl. Возможно, кто-то еще тоже...
Вот функция, которая выполняет perl-совместимые регулярные выражения и соответствует функциональности функций на других языках, к которым я привык:
regexpr_perl <- function(expr, str) {
match <- regexpr(expr, str, perl=T)
matches <- character(0)
if (attr(match, 'match.length') >= 0) {
capture_start <- attr(match, 'capture.start')
capture_length <- attr(match, 'capture.length')
total_matches <- 1 + length(capture_start)
matches <- character(total_matches)
matches[1] <- substr(str, match, match + attr(match, 'match.length') - 1)
if (length(capture_start) > 1) {
for (i in 1:length(capture_start)) {
matches[i + 1] <- substr(str, capture_start[[i]], capture_start[[i]] + capture_length[[i]] - 1)
}
}
}
matches
}
Ответ 6
Вот как я закончил работу над этой проблемой. Я использовал два отдельных регулярных выражения для соответствия первой и второй группам захвата и запускал два вызова gregexpr
, затем вытащил подстрочные подстроки:
regex.string <- "(?<=\\().*?(?= :: )"
regex.number <- "(?<= :: )\\d\\.\\d+"
match.string <- gregexpr(regex.string, str, perl=T)[[1]]
match.number <- gregexpr(regex.number, str, perl=T)[[1]]
strings <- mapply(function (start, len) substr(str, start, start+len-1),
match.string,
attr(match.string, "match.length"))
numbers <- mapply(function (start, len) as.numeric(substr(str, start, start+len-1)),
match.number,
attr(match.number, "match.length"))
Ответ 7
Как показано в пакете stringr
, это может быть достигнуто с помощью str_match()
или str_extract()
.
Адаптировано из руководства:
library(stringr)
strings <- c(" 219 733 8965", "329-293-8753 ", "banana",
"239 923 8115 and 842 566 4692",
"Work: 579-499-7527", "$1000",
"Home: 543.355.3679")
phone <- "([2-9][0-9]{2})[- .]([0-9]{3})[- .]([0-9]{4})"
Извлечение и объединение наших групп:
str_extract(strings, phone)
# [1] "219 733 8965" "329-293-8753" NA "239 923 8115" "579-499-7527" NA
# [7] "543.355.3679"
Указание групп с выходной матрицей (нас интересуют столбцы 2 +):
str_match(strings, phone)
# [,1] [,2] [,3] [,4]
# [1,] "219 733 8965" "219" "733" "8965"
# [2,] "329-293-8753" "329" "293" "8753"
# [3,] NA NA NA NA
# [4,] "239 923 8115" "239" "923" "8115"
# [5,] "579-499-7527" "579" "499" "7527"
# [6,] NA NA NA NA
# [7,] "543.355.3679" "543" "355" "3679"
Ответ 8
Решение с strcapture
из utils
:
x <- c("key1 :: 0.01",
"key2 :: 0.02")
strcapture(pattern = "(.*) :: (0\\.[0-9]+)",
x = x,
proto = list(key = character(), value = double()))
#> key value
#> 1 key1 0.01
#> 2 key2 0.02