110 votes

Capture de groupes Regex dans R avec plusieurs groupes de capture

Dans R, est-il possible d'extraire une capture de groupe à partir d'une correspondance d'expression régulière ? D'après ce que je sais, aucune grep , grepl , regexpr , gregexpr , sub ou gsub retourner les captures du groupe.

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

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

Je peux toujours faire plusieurs greps complets, ou faire un traitement externe (non-R), mais j'espérais pouvoir faire tout cela dans R. Y a-t-il une fonction ou un paquet qui fournit une telle fonction pour faire cela ?

133voto

Kent Johnson Points 427

str_match() à partir du stringr va le faire. Il renvoie une matrice de caractères avec une colonne pour chaque groupe de la correspondance (et une pour l'ensemble de 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"

2 votes

Et str_match_all() pour faire correspondre tous les groupes dans une regex

0 votes

Comment puis-je imprimer uniquement les groupes capturés pour [,1] ?

1 votes

Je ne sais pas ce que vous cherchez. Les groupes capturés sont les colonnes 2 et 3. [,1] est la correspondance complète. [,2:3] est le groupe capturé.

68voto

Gsub le fait, d'après votre exemple :

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

vous devez doublement échapper le \s dans les guillemets alors ils fonctionnent pour la regex.

J'espère que cela vous aidera.

0 votes

En fait, j'ai besoin d'extraire les sous-chaînes capturées pour les mettre dans un data.frame. Mais, en regardant votre réponse, je suppose que je pourrais enchaîner gsub et quelques strsplit pour obtenir ce que je veux, peut-être : strsplit(strsplit(gsub(regex, " \\1 : : \\2 ::: :", str), "::: :")[[1]], ": :")

13 votes

Super. Le R gsub a vraiment besoin d'un exemple montrant que vous avez besoin de ' \\1 pour échapper à une référence de groupe de capture.

46voto

jeales Points 41

Essayez regmatches() y regexec() :

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

7 votes

Merci pour la solution de vanilla R et pour avoir indiqué regmatches que je n'ai jamais vu auparavant

0 votes

Pourquoi devriez-vous écrire la chaîne deux fois ?

1 votes

@StefanoBorini regexec renvoie une liste contenant des informations concernant uniquement l'emplacement des correspondances, donc regmatches demande à l'utilisateur de fournir la chaîne de caractères à laquelle appartient la liste de correspondance.

21voto

alsocasey Points 53

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

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

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

Ainsi, si le texte à sélectionner se trouve au milieu d'une chaîne de caractères, l'ajout de .* avant et après le groupe de capture devrait vous permettre de ne renvoyer que celui-ci.

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

4voto

Daniel Dickison Points 15182

C'est ainsi que j'ai fini par contourner ce problème. J'ai utilisé deux regex distinctes pour faire correspondre le premier et le deuxième groupe de capture et j'ai exécuté deux fois la commande 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"))

0 votes

+1 pour un code qui fonctionne. Cependant, je préfère exécuter une commande shell rapide à partir de R et utiliser une ligne unique Bash comme celle-ci expr "xyx0.0023xyxy" : '[^0-9]*\([.0-9]\+\)'

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X