2009-06-04 16 views
67

En R, ¿es posible extraer la captura de grupo de una coincidencia de expresión regular? Por lo que puedo decir, ninguno de grep, grepl, regexpr, gregexpr, sub, o gsub devuelven las capturas de grupo.Captura de grupo Regex en R con múltiples grupos de captura

I necesidad de extraer pares de valores clave de cadenas que están codificados por lo tanto:

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

I siempre puede hacer múltiples greps-partido completo, o hacer algo de procesamiento exterior (no-R), pero estaba esperando poder hacerlo todo dentro de R. ¿Hay una función o un paquete que brinde tal función para hacer esto?

Respuesta

88

str_match(), del paquete stringr, lo hará. Devuelve una matriz de caracteres con una columna para cada grupo en el partido (y uno para todo el partido):

> 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

Esto es, de hecho, exactamente lo que necesitaba (cuando hice la pregunta originalmente). Marcado como aceptado para referencias futuras. Gracias. –

+0

y 'str_match_all()' para unir todos los grupos en una expresión regular – smci

34

gsub hace esto, de su ejemplo:

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

que necesita para duplicar escapar de la \ s en las citas a continuación, para las que trabajan la expresión regular.

Espero que esto ayude.

+0

En realidad tengo que sacar las subcadenas capturadas para ponerlas en un data.frame. Pero, mirando su respuesta, creo que podría encadenar gsub y un par de strsplit para obtener lo que quiero, tal vez: strsplit (strsplit (gsub (regex, "\\ 1 :: \\ 2 ::::" , str), "::::") [[1]], "::") –

+5

Genial. La página de manual de R 'gsub' necesita urgentemente un ejemplo que muestre que necesita '\\ 1' para escapar de una referencia de grupo de captura. – smci

2

Así es como terminé solucionando este problema. He utilizado dos expresiones regulares separadas para que coincida con el primer y segundo grupos de captura y ejecutar dos gregexpr llamadas, a continuación, extraiga las subcadenas coincidentes:

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 para un código de trabajo. Sin embargo, prefiero ejecutar un comando de shell rápido desde R y usar un Bash one-liner como este 'expr" xyx0.0023xyxy ": '[^ 0-9] * \ ([. 0-9] \ + \) '' –

15

gsub() puede hacer esto y devolver sólo el grupo de captura:

Sin embargo, para que esto funcione, debe seleccionar explícitamente elementos fuera de su grupo de captura como se menciona en la ayuda de gsub().

(...) los elementos de los vectores de caracteres 'x' que no se sustituyan se devolverán sin cambios.

Por lo tanto, si el texto que va a seleccionar se encuentra en el medio de una cadena, agregar. * Antes y después del grupo de captura debería permitirle solo devolverlo.

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

16

Trate regmatches() y regexec():

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

Gracias por la solución R de vanilla y por señalar los "regmatches" que nunca he visto antes – Andy

3

me gustan las expresiones regulares compatibles con Perl. Es probable que alguien más lo hace demasiado ...

Aquí es una función que hace Perl expresiones regulares compatibles y coincide con la funcionalidad de las funciones en otros idiomas que yo estoy acostumbrado a:

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

Como se sugiere en el paquete stringr , esto se puede lograr utilizando str_match() o str_extract().

adaptado del manual de:

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})" 

La extracción y la combinación de nuestros grupos:

str_extract(strings, phone) 
# [1] "219 733 8965" "329-293-8753" NA    "239 923 8115" "579-499-7527" NA    
# [7] "543.355.3679" 

grupos indicadores con una matriz de salida (que estamos interesados ​​en las columnas 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

Solución con strcapture del 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 
Cuestiones relacionadas