2421 votes

Qu'est-ce qu'un groupe non capturant dans les expressions régulières ?

Comment les groupes qui ne capturent pas, c'est à dire (?:) utilisés dans les expressions régulières et à quoi servent-ils ?

70 votes

Cette question a été ajoutée à la FAQ sur les expressions régulières de Stack Overflow sous la rubrique "Groupes".

2982voto

Ricardo Nolde Points 5630

Je vais essayer d'expliquer cela à l'aide d'un exemple.

Considérez le texte suivant :

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

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

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

Vous voyez ? Le premier groupe n'a pas été saisi. L'analyseur syntaxique l'utilise pour faire correspondre le texte, mais l'ignore ensuite, dans le résultat final.


EDITAR:

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

Les groupes ont plusieurs fonctions. Ils peuvent vous aider à extraire des informations exactes d'une correspondance plus importante (qui peut également être nommée), ils vous permettent de faire correspondre à nouveau un groupe correspondant précédent, et peuvent être utilisés pour des substitutions. Essayons quelques exemples, d'accord ?

Imaginez que vous avez une sorte de fichier XML ou HTML (sachez que regex n'est peut-être pas le meilleur outil pour ce travail mais c'est un bon exemple). Vous voulez analyser les balises, vous pourriez donc faire quelque chose comme ceci (j'ai ajouté des espaces pour faciliter la compréhension) :

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

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

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

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

Maintenant, utilisons cette stupide regex sur ça :

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

Cette regex correspond à des mots comportant 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 serait comme 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 les éléments suivants ${name} .

Pour jouer avec les regex, je recommande http://regex101.com/ qui offre une bonne quantité de détails sur le fonctionnement des regex ; il propose également quelques moteurs de regex parmi lesquels choisir.

4 votes

@ajsie : Les groupes traditionnels (capture) sont plus utiles si vous effectuez une opération de remplacement sur les résultats. Voici un exemple où je saisis les noms et prénoms séparés par des virgules, puis j'inverse leur ordre (grâce aux groupes nommés)... regexhero.net/tester/?id=16892996-64d4-4f10-860a-24f28dad7e3‌​0

0 votes

Puis-je l'utiliser comme ceci ? ([? :]http|ftp)://([^/ \r\n ]+)(/[^ \r\n ]*) ? Est-ce la même chose que (?:http|ftp)://([^/ \r\n ]+)(/[^ \r\n ]*) ? . veuillez répondre rapidement

6 votes

On peut également souligner que les groupes non capturants sont particulièrement utiles lorsqu'on utilise des expressions rationnelles comme délimiteurs de fractionnement : "Alice et Bob"-split" \s +(?:et|ou) \s +"

243voto

Bill the Lizard Points 147311

Vous pouvez utiliser des groupes de capture pour organiser et analyser une expression. Un groupe non capturant présente le 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 un texte numérique, mais que certains nombres puissent être écrits comme 1er, 2ème, 3ème, 4ème, .... Si vous voulez capturer la partie numérique, mais pas le suffixe (facultatif), vous pouvez utiliser un groupe de non-capture.

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

Cela correspondra aux nombres sous la forme 1, 2, 3... ou sous la forme 1st, 2nd, 3rd,... mais cela ne capturera que la partie numérique.

2 votes

Sans le groupe de non-capture, je pourrais faire : ([0-9]+)(st|nd|rd|th)? ? Avec \1 J'ai le numéro, non ?: nécessaire. BTW, quel est le ? à la fin ?

157voto

RC. Points 15804

?: est utilisé lorsque vous voulez regrouper une expression, mais que vous ne voulez pas l'enregistrer comme une partie de la chaîne de caractères correspondant à l'expression ou la capturant.

Un exemple serait quelque chose qui correspondrait à une adresse IP :

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

Notez que je ne me soucie pas de sauvegarder les 3 premiers octets, mais les (?:...) Le regroupement me permet de raccourcir la regex sans avoir à supporter les frais de capture et de stockage d'une correspondance.

17 votes

Pour les lecteurs inexpérimentés : Cela correspondrait à une adresse IP, mais aussi à des adresses IP invalides. Une expression pour valider une adresse IP serait beaucoup plus complexe. Ne l'utilisez donc pas pour valider une adresse IP.

47voto

sepp2k Points 157757

Elle rend le groupe non capturant, ce qui signifie que la sous-chaîne correspondant à 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"]

0 votes

Pourquoi ne pouvons-nous pas simplement utiliser "abc".match(/.(.)./).captures ici ?

1 votes

@PRASANNASARAF Vous pouvez, bien sûr. Le but de ce code était de montrer que (?:) ne produit pas de capture, pour ne pas démontrer un exemple utile de (?:) . (?:) est utile lorsque vous voulez regrouper une sous-expression (par exemple, lorsque vous voulez appliquer des quantificateurs à une sous-expression non atomique ou si vous voulez restreindre la portée d'une expression de type | ), mais vous ne voulez pas capturer quoi que ce soit.

23voto

Bob Fincheimer Points 6849

Les groupes qui capture que vous pouvez utiliser plus tard dans l'expression rationnelle pour faire correspondre OU vous pouvez les utiliser dans la partie de remplacement de la regex. Faire une non-capture exempte simplement ce groupe d'être utilisé pour l'une ou l'autre de ces raisons.

Les groupes de non-capture sont parfaits si vous essayez de capturer de nombreuses choses différentes et qu'il y a certains groupes que vous ne voulez pas capturer.

C'est à peu près la raison de leur existence. Pendant que vous apprenez à connaître les groupes, apprenez à connaître Groupes atomiques ils font beaucoup ! Il existe également des groupes de surveillance, mais ils sont un peu plus complexes et moins utilisés.

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

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

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

Plus loin dans la regex, il y a \1 ce qui signifie qu'il ne correspondra qu'au même texte que celui qui se trouvait dans le premier groupe (le groupe ([A-Z][A-Z0-9]*) ) (dans ce cas, il s'agit de faire correspondre la balise de fin).

1 votes

Pourriez-vous donner un exemple simple de la façon dont il sera utilisé ultérieurement pour correspondre à l'OR ?

1 votes

Je veux dire que vous pouvez l'utiliser pour l'assortir plus tard ou vous pouvez l'utiliser dans le remplacement. Le ou dans cette phrase était juste pour vous montrer qu'il y a deux utilisations pour un groupe de capture.

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