web-dev-qa-db-fra.com

Qu'est-ce qu'un groupe non capturant? Qu'est-ce que (? :) fait?

Comment ?: est utilisé et à quoi sert-il?

1462
never_had_a_name

Laissez-moi essayer d'expliquer cela avec un exemple.

Considérez le texte suivant:

http://stackoverflow.com/
https://stackoverflow.com/questions/tagged/regex

Maintenant, si j'applique la regex ci-dessous dessus ...

(https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?

... j'obtiendrais le résultat suivant:

Match "http://stackoverflow.com/"
     Group 1: "http"
     Group 2: "stackoverflow.com"
     Group 3: "/"

Match "https://stackoverflow.com/questions/tagged/regex"
     Group 1: "https"
     Group 2: "stackoverflow.com"
     Group 3: "/questions/tagged/regex"

Mais je ne me soucie pas du protocole - je veux juste l'hôte et le chemin de l'URL. Donc, je change la regex pour inclure le groupe non-capturant (?:).

(?:https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?

Maintenant, mon résultat ressemble à ceci:

Match "http://stackoverflow.com/"
     Group 1: "stackoverflow.com"
     Group 2: "/"

Match "https://stackoverflow.com/questions/tagged/regex"
     Group 1: "stackoverflow.com"
     Group 2: "/questions/tagged/regex"

Voir? Le premier groupe n'a pas été capturé. L'analyseur l'utilise pour faire correspondre le texte, mais l'ignore ultérieurement dans le résultat final.


MODIFIER:

Comme demandé, permettez-moi d'essayer d'expliquer les groupes aussi.

Les groupes ont plusieurs objectifs. Ils peuvent vous aider à extraire des informations exactes d'une correspondance plus grande (qui peut également être nommée), ils vous permettent de ré-apparier un groupe précédemment mis en correspondance et peuvent être utilisés pour des substitutions. Essayons quelques exemples, allons-nous?

Ok, imaginez que vous ayez une sorte de XML ou HTML (sachez que regex n'est peut-être pas le meilleur outil pour le travail , mais c'est Nice comme exemple). Vous voulez analyser les balises pour pouvoir faire quelque chose comme ceci (j'ai ajouté des espaces pour le rendre plus facile à comprendre):

   \<(?<TAG>.+?)\> [^<]*? \</\k<TAG>\>
or
   \<(.+?)\> [^<]*? \</\1\>

La première expression rationnelle a un groupe nommé (TAG), tandis que la seconde utilise un groupe commun. Les deux regex font la même chose: ils utilisent la valeur du premier groupe (le nom de la balise) pour correspondre à la balise de fermeture. La différence est que le premier utilise le nom pour correspondre à la valeur et le second utilise l'index du groupe (qui commence à 1).

Essayons quelques substitutions maintenant. Considérez le texte suivant:

Lorem ipsum dolor sit amet consectetuer feugiat fames malesuada pretium egestas.

Maintenant, utilisons le regex idiot dessus:

\b(\S)(\S)(\S)(\S*)\b

Cette expression rationnelle associe des mots d'au moins 3 caractères et utilise des groupes pour séparer les trois premières lettres. Le résultat est le suivant:

Match "Lorem"
     Group 1: "L"
     Group 2: "o"
     Group 3: "r"
     Group 4: "em"
Match "ipsum"
     Group 1: "i"
     Group 2: "p"
     Group 3: "s"
     Group 4: "um"
...

Match "consectetuer"
     Group 1: "c"
     Group 2: "o"
     Group 3: "n"
     Group 4: "sectetuer"
...

Donc, si nous appliquons la chaîne de substitution ...

$1_$3$2_$4

... par-dessus, nous essayons d'utiliser le premier groupe, d'ajouter un trait de soulignement, d'utiliser le troisième groupe, puis le deuxième groupe, d'ajouter un autre trait de soulignement, puis le quatrième groupe. La chaîne résultante ressemblerait à celle ci-dessous.

L_ro_em i_sp_um d_lo_or s_ti_ a_em_t c_no_sectetuer f_ue_giat f_ma_es m_la_esuada p_er_tium e_eg_stas.

Vous pouvez également utiliser des groupes nommés pour les substitutions, en utilisant ${name}.

Pour jouer avec les expressions rationnelles, je recommande http://regex101.com/ , qui offre une bonne quantité de détails sur le fonctionnement de l’expression rationnelle; il offre également quelques moteurs de regex à choisir.

1994
Ricardo Nolde

Vous pouvez utiliser des groupes de capture pour organiser et analyser une expression. Un groupe non capturant bénéficie du premier avantage, mais n'a pas les frais généraux du second. Vous pouvez toujours dire qu'un groupe non capturant est facultatif, par exemple.

Supposons que vous vouliez faire correspondre le texte numérique, mais que certains chiffres pourraient être écrits en tant que 1er, 2ème, 3ème, 4ème, ... Si vous souhaitez capturer la partie numérique, mais pas le suffixe (facultatif), vous pouvez utiliser un groupe non capturant. .

([0-9]+)(?:st|nd|rd|th)?

Cela correspondra aux numéros sous la forme 1, 2, 3 ... ou sous la forme 1er, 2ème, 3ème, ... mais il ne capturera que la partie numérique.

143
Bill the Lizard

?: est utilisé lorsque vous souhaitez grouper une expression, mais vous ne souhaitez pas l'enregistrer en tant que partie correspondante/capturée de la chaîne.

Un exemple serait quelque chose pour correspondre à une adresse IP:

/(?:\d{1,3}\.){3}\d{1,3}/

Notez que je ne me soucie pas de sauvegarder les 3 premiers octets, mais le groupement (?:...) me permet de raccourcir l'expression rationnelle sans encourir la surcharge de la capture et du stockage d'une correspondance. 

93
RC.

Cela rend le groupe non capturant, ce qui signifie que la sous-chaîne trouvée par ce groupe ne sera pas incluse dans la liste des captures. Un exemple en Ruby pour illustrer la différence:

"abc".match(/(.)(.)./).captures #=> ["a","b"]
"abc".match(/(?:.)(.)./).captures #=> ["b"]
30
sepp2k

MOTIVATION HISTORIQUE: L'existence de groupes sans capture peut être expliquée avec l'utilisation de parenthèses. Considérons les expressions (a | b) c et a | bc, en raison de la priorité de la concaténation sur |, ces expressions représentent deux langues différentes ({ac, bc} et {a, bc} respectivement). Cependant, les parenthèses sont également utilisées comme groupe d'appariement (comme l'expliquent les autres réponses ...).

Lorsque vous souhaitez mettre entre parenthèses mais pas capturer la sous-expression, vous utilisez des groupes non capturant. Dans l'exemple, (?: A | b) c

16
user2369060

Les groupes que capture vous pourrez utiliser plus tard dans l'expression régulière pour correspondre à OU peuvent être utilisés dans la partie de remplacement de l'expression régulière. Créer un groupe non capturant exempte simplement l'utilisation de ce groupe pour l'une ou l'autre de ces raisons. 

Les groupes sans capture sont parfaits si vous essayez de capturer différentes choses et que vous ne souhaitez pas capturer certains groupes. 

C'est à peu près la raison pour laquelle ils existent. Pendant que vous étudiez les groupes, découvrez Atomic Groups , ils font beaucoup! Il existe également des groupes de lookaround, mais ils sont un peu plus complexes et peu utilisés.

Exemple d'utilisation ultérieure dans la regex (backreference):

<([A-Z][A-Z0-9]*)\b[^>]*>.*?</\1> [Recherche une balise XML (sans support ns)]

([A-Z][A-Z0-9]*) est un groupe de capture (dans ce cas, c'est le nom de la variable)

Plus tard dans l'expression rationnelle, il y a \1, ce qui signifie qu'il ne fera que correspondre au même texte que celui du premier groupe (le groupe ([A-Z][A-Z0-9]*)) (dans ce cas, il correspond à la balise de fin).

13
Bob Fincheimer

Laissez-moi essayer ceci avec un exemple: -

Code de regex: - (?:animal)(?:=)(\w+)(,)\1\2

Chaîne de recherche: -

Ligne 1 - animal=cat,dog,cat,tiger,dog

Ligne 2 - animal=cat,cat,dog,dog,tiger

Ligne 3 - animal=dog,dog,cat,cat,tiger

(?:animal) -> Groupe 1 non capturé

(?:=)--> Groupe 2 non capturé

(\w+)--> Capturé Groupe 1

(,)--> Capturé Groupe 2

\1 -> résultat du groupe capturé 1: la ligne 1 est un chat, la ligne 2 est un chat, la ligne 3 est un chien.

\2 -> résultat du groupe capturé 2 i.e virgule (,)

Donc, dans ce code en donnant\1 et\2, nous rappelons ou répétons le résultat des groupes capturés 1 et 2 respectivement plus tard dans le code.

Selon l'ordre du code (?: Animal) devrait être le groupe 1 et (?: =) Devrait être le groupe 2 et continue.

mais en donnant le signe?: nous faisons en sorte que le groupe de match ne soit pas capturé (ce qui ne compte pas dans le groupe apparié, le numéro de groupe commence donc à partir du premier groupe capturé et non du non capturé), de sorte que -group (?: animal) ne peut pas être appelé plus tard dans le code.

J'espère que cela explique l'utilisation de groupe non capturant.

entrez la description de l'image ici

10
shekhar gehlot

Eh bien, je suis un développeur JavaScript et vais essayer d’expliquer son importance pour JavaScript.

Envisagez un scénario dans lequel vous souhaitez faire correspondre le cat is animal Lorsque vous souhaitez associer un chat et un animal et que les deux doivent avoir une is entre eux.

 // this will ignore "is" as that's is what we want
"cat is animal".match(/(cat)(?: is )(animal)/) ;
result ["cat is animal", "cat", "animal"]

 // using lookahead pattern it will match only "cat" we can
 // use lookahead but the problem is we can not give anything
 // at the back of lookahead pattern
"cat is animal".match(/cat(?= is animal)/) ;
result ["cat"]

 //so I gave another grouping parenthesis for animal
 // in lookahead pattern to match animal as well
"cat is animal".match(/(cat)(?= is (animal))/) ;
result ["cat", "cat", "animal"]

 // we got extra cat in above example so removing another grouping
"cat is animal".match(/cat(?= is (animal))/) ;
result ["cat", "animal"]
6
Gaurav

Dans les expressions rationnelles complexes, il peut arriver que vous souhaitiez utiliser un grand nombre de groupes, dont certains sont destinés à la correspondance de répétition et certains sont destinés à fournir des références en arrière. Par défaut, le texte correspondant à chaque groupe est chargé dans le tableau de références arrières. Lorsque nous avons beaucoup de groupes et que nous n'avons besoin que de pouvoir référencer certains d'entre eux à partir du tableau de références arrière, nous pouvons redéfinir ce comportement par défaut pour indiquer à l'expression régulière que certains groupes ne sont là que pour la gestion de la répétition et n'ont pas besoin d'être capturés et stockés dans le tableau de référence.

5
Jack Peng

Une chose intéressante que j'ai découverte est le fait que vous pouvez avoir un groupe de capture au sein d'un groupe sans capture. Consultez ci-dessous les expressions rationnelles pour les URL Web correspondantes:

var parse_url_regex = /^(?:([A-Za-z]+):)(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;

Chaîne d'URL d'entrée:

var url = "http://www.ora.com:80/goodparts?q#fragment";

Le premier groupe de ma regex (?:([A-Za-z]+):) est un groupe non capturant qui correspond au schéma de protocole et au caractère deux-points :, c’est-à-dire http:, mais lorsque j’exécutais le code, le premier index du tableau renvoyé contenait la chaîne http lorsque en pensant que http et deux-points : ne seront tous deux pas signalés car ils font partie d'un groupe non capturant.

console.debug(parse_url_regex.exec(url));

 enter image description here

Je pensais que si le premier groupe (?:([A-Za-z]+):) était un groupe non capturant, il renvoyait donc la chaîne http dans le tableau de sortie.

Donc, si vous remarquez qu'il y a un groupe imbriqué ([A-Za-z]+) dans le groupe sans capture. Ce groupe imbriqué ([A-Za-z]+) est un groupe de capture (ne pas avoir ?: au début) en lui-même à l'intérieur d'un groupe non-capture (?:([A-Za-z]+):). C'est pourquoi le texte http est toujours capturé, mais le caractère deux-points : qui se trouve à l'intérieur du groupe non capturé mais à l'extérieur du groupe capturant n'est pas signalé dans le tableau de sortie.

4
RBT

tl; dr groupes non capturés, comme son nom l'indique, sont les parties de l'expression rationnelle que vous ne souhaitez pas inclure dans la correspondance et ?: est un moyen de définir un groupe comme ne faisant pas l'objet d'une capture.

Disons que vous avez une adresse électronique [email protected]. La regex suivante créera deux groupes , la partie id et la partie @ example.com. (\p{Alpha}*[a-z])(@example.com). Pour simplifier, nous extrayons le nom de domaine complet, y compris le caractère @.

Maintenant, disons que vous n’avez besoin que de la partie id de l’adresse. Ce que vous voulez faire est de saisir le premier groupe du résultat de la correspondance, entouré de () dans la regex. Pour ce faire, utilisez la syntaxe de groupe sans capture, c.-à-d. ?:. Ainsi, l'expression régulière (\p{Alpha}*[a-z])(?:@example.com) renverra uniquement la partie id de l'e-mail.

4
6pack kid

Je ne peux pas commenter les réponses les plus fréquentes pour dire ceci: je voudrais ajouter un point explicite qui n’est impliqué que dans les réponses les plus fréquentes:

Le groupe non-capturant (?...) Ne supprime aucun caractère de la correspondance complète d'origine, il seulement réorganise visuellement l'expression régulière pour le programmeur. 

Pour accéder à une partie spécifique de l'expression rationnelle sans caractères étrangers définis, vous devez toujours utiliser .group(<index>).

4
Scott Anderson

Je pense que je vous donnerais la réponse, N'utilisez pas de variables de capture sans vérifier que la correspondance a réussi.

Les variables de capture, $ 1, etc., ne sont valides que si la correspondance est réussie et elles ne sont pas non plus effacées.

#!/usr/bin/Perl  
use warnings;
use strict;   
$_ = "bronto saurus burger";
if (/(?:bronto)? saurus (steak|burger)/)
{
    print "Fred wants a  $1";
}
else
{
    print "Fred dont wants a $1 $2";
}

Dans l'exemple ci-dessus, pour éviter de capturer bronto dans $ 1, (? :) est utilisé . Si le motif correspond, alors $ 1 est capturé comme le prochain motif groupé . Ainsi, le résultat sera comme ci-dessous:

Fred wants a burger

C'est utile si vous ne voulez pas que les matchs soient sauvegardés.

1
Harini

Ouvrez votre Google Chrome devTools, puis l'onglet Console: et tapez ceci:

"Peace".match(/(\w)(\w)(\w)/)

Exécutez-le et vous verrez:

["Pea", "P", "e", "a", index: 0, input: "Peace", groups: undefined]

Le moteur JavaScript RegExp capture trois groupes, les éléments avec des index 1,2,3. Utilisez maintenant une marque non capturante pour voir le résultat.

"Peace".match(/(?:\w)(\w)(\w)/)

Le résultat est:

["Pea", "e", "a", index: 0, input: "Peace", groups: undefined]

Ceci est évident quel est le groupe non capturant.

0
AmerllicA

C'est extrêmement simple, nous pouvons comprendre avec un exemple de date simple, supposons que la date soit mentionnée le 1er janvier 2019 ou le 2 mai 2019 ou toute autre date et que nous souhaitons simplement la convertir au format jj/mm/aaaa pas besoin du nom du mois qui est janvier ou février d'ailleurs, donc pour capturer la partie numérique, mais pas le suffixe (facultatif), vous pouvez utiliser un groupe non capturant.

donc l'expression régulière serait,

([0-9]+)(?:January|February)?

C'est aussi simple que ça.

0
Naved Ahmad