Захват Regex группы в R с несколькими группами захвата - PullRequest
86 голосов
/ 04 июня 2009

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

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

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

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

Ответы [ 8 ]

110 голосов
/ 06 апреля 2012

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"    
43 голосов
/ 05 июня 2009

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

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

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

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

29 голосов
/ 15 мая 2013

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

regmatches("(sometext :: 0.1231313213)",regexec("\\((.*?) :: (0\\.[0-9]+)\\)","(sometext :: 0.1231313213)"))
[[1]]
[1] "(sometext :: 0.1231313213)" "sometext"                   "0.1231313213"
18 голосов
/ 27 апреля 2011

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

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

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

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

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

4 голосов
/ 29 января 2015

Мне нравятся 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
}
3 голосов
/ 05 июня 2009

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

Решение с 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
2 голосов
/ 23 декабря 2015

Как указано в пакете 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_all(strings, phone, simplify=T)
#      [,1]           [,2]          
# [1,] "219 733 8965" ""            
# [2,] "329-293-8753" ""            
# [3,] ""             ""            
# [4,] "239 923 8115" "842 566 4692"
# [5,] "579-499-7527" ""            
# [6,] ""             ""            
# [7,] "543.355.3679" ""   

Указание групп с выходной матрицей (нас интересуют столбцы 2+):

str_match_all(strings, phone)
# [[1]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "219 733 8965" "219" "733" "8965"
# 
# [[2]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "329-293-8753" "329" "293" "8753"
# 
# [[3]]
#      [,1] [,2] [,3] [,4]
# 
# [[4]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "239 923 8115" "239" "923" "8115"
# [2,] "842 566 4692" "842" "566" "4692"
# 
# [[5]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "579-499-7527" "579" "499" "7527"
# 
# [[6]]
#      [,1] [,2] [,3] [,4]
# 
# [[7]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "543.355.3679" "543" "355" "3679"
...