2009-06-04 3 views
67

В R можно извлечь групповой захват из регулярного выражения? Насколько я могу судить, ни один из grep, grepl, regexpr, gregexpr, sub, или gsub вернуть группы.Групповой захват группы в R с несколькими группами захвата

Мне нужно извлечь пары ключ-значение из строки, которые закодированы таким образом:

\((.*?) :: (0\.[0-9]+)\) 

я всегда могу просто сделать несколько отбирает полный матч, или сделать какой-либо внешней (не R) обработки, но я надеялся, что смогу сделать все это в пределах R. Есть ли функция или пакет, который предоставляет такую ​​функцию для этого?

ответ

88

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"  
+1

Это на самом деле то, что мне нужно (назад, когда я задал вопрос первоначально). Маркировка, принятая для использования в будущем. Благодарю. –

+0

и 'str_match_all()' для соответствия всем группам в регулярном выражении – smci

34

GSUB делает это, из вашего примера:

gsub("\\((.*?) :: (0\\.[0-9]+)\\)","\\1 \\2", "(sometext :: 0.1231313213)") 
[1] "sometext 0.1231313213" 

вам нужно удвоить спасаясь от \ s в кавычки, то они работают на регулярных выражений.

Надеюсь, что это поможет.

+0

На самом деле мне нужно вытащить захваченные подстроки, чтобы положить в data.frame. Но, глядя на ваш ответ, я думаю, я мог бы цепочки gsub и пару strsplit, чтобы получить то, что я хочу, может быть: strsplit (strsplit (gsub (regex, "\\ 1 :: \\ 2 ::::" , str), "::::") [[1]], "::") –

+5

Отлично. В man-странице R 'gsub' очень нужен пример, показывающий, что вам нужно« \\ 1 », чтобы избежать ссылки группы захвата. – smci

2

Вот как я столкнулся с этой проблемой. Я использовал два отдельных регулярных выражений, чтобы соответствовать первой и второй группы захвата и запуска двух 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")) 
+0

+1 для рабочего кода. Тем не менее, я предпочел бы запустить команду быстрого shell из R и использовать однострочный Bash как этот 'expr 'xyx0.0023xyxy":' [^ 0-9] * \ ([. 0-9] \ + \) '' –

15

GSUB() может сделать это и возвращать только группу захвата:

Однако для того, чтобы это работало, вы должны явно выбирать элементы вне вашей группы захвата, как указано в справке gsub().

(...) элементы символьных векторов 'x', которые не заменены, будут возвращены без изменений.

Поэтому, если ваш текст будет выбран в середине некоторой строки, добавление. * До и после группы захвата должно позволить вам только вернуть ее.

gsub(".*\\((.*?) :: (0\\.[0-9]+)\\).*","\\1 \\2", "(sometext :: 0.1231313213)") [1] "sometext 0.1231313213"

16

Попробуйте regmatches() и regexec():

regmatches("(sometext :: 0.1231313213)",regexec("\\((.*?) :: (0\\.[0-9]+)\\)","(sometext :: 0.1231313213)")) 
[[1]] 
[1] "(sometext :: 0.1231313213)" "sometext"     "0.1231313213" 
+1

Спасибо за решение vanilla R и за указание 'regmatches', которого я никогда раньше не видел – Andy

3

Мне нравится 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 
} 
0

Как предложено в 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" 
0

Решение с 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 
Смежные вопросы