web-dev-qa-db-fra.com

Capture de groupe d'expression régulière dans R avec plusieurs groupes de capture

Dans R, est-il possible d'extraire la capture de groupe d'une correspondance d'expression régulière? Pour autant que je sache, aucun de grep, grepl, regexpr, gregexpr, sub ou gsub retourner les captures de groupe.

J'ai besoin d'extraire des paires clé-valeur de chaînes qui sont encodées ainsi:

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

Je peux toujours simplement faire plusieurs greps en correspondance complète, ou faire un traitement extérieur (non R), mais j'espérais pouvoir tout faire dans R. Y a-t-il une fonction ou un package qui fournit une telle fonction pour ce faire?

87
Daniel Dickison

str_match(), du paquet stringr , fera cela. Il renvoie une matrice de caractères avec une colonne pour chaque groupe de la correspondance (et une pour toute la correspondance):

> 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"    
110
Kent Johnson

gsub fait cela, à partir de votre exemple:

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

vous devez double échapper les\s entre guillemets, puis ils fonctionnent pour l'expression régulière.

J'espère que cela t'aides.

44

Essayez regmatches() et regexec():

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

gsub () peut le faire et retourner uniquement le groupe de capture:

Cependant, pour que cela fonctionne, vous devez explicitement sélectionner des éléments en dehors de votre groupe de capture comme indiqué dans l'aide de gsub ().

(...) les éléments des vecteurs de caractères 'x' qui ne sont pas substitués seront retournés inchangés.

Donc, si votre texte à sélectionner se trouve au milieu d'une chaîne, l'ajout de. * Avant et après le groupe de capture devrait vous permettre de le renvoyer uniquement.

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

18
cashoes

J'aime les expressions régulières compatibles Perl. Probablement quelqu'un d'autre aussi ...

Voici une fonction qui fait des expressions régulières compatibles Perl et correspond à la fonctionnalité des fonctions dans d'autres langages auxquels je suis habitué:

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
}
4
ruffbytes

C'est ainsi que j'ai fini par contourner ce problème. J'ai utilisé deux expressions régulières distinctes pour faire correspondre les premier et deuxième groupes de capture et exécuter deux appels gregexpr, puis extraire les sous-chaînes correspondantes:

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"))
3
Daniel Dickison

Comme suggéré dans le package stringr , cela peut être réalisé en utilisant str_match() ou str_extract().

Adapté du manuel:

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

Extraire et combiner nos groupes:

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

Indiquer des groupes avec une matrice de sortie (nous sommes intéressés par les colonnes 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"
2
Megatron

Solution avec strcapture dans le 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
Artem Klevtsov