Rétroréférences dans les expressions régulières : \n et \k<name>
Découvrez les rétroréférences dans les expressions régulières JavaScript : groupes numérotés, groupes nommés et utilisation dans replace().
La plupart d'une expression régulière correspond à un texte fixe, mais il arrive que vous ayez besoin de faire correspondre un texte qui doit être identique à quelque chose que vous avez capturé précédemment — sans savoir à l'avance de quoi il s'agit. Une rétroréférence résout exactement ce problème. Elle permet à un motif de dire « fais correspondre la même chose que ce que le groupe a capturé un instant plus tôt. »
Les usages classiques des rétroréférences incluent la détection d'un mot doublé (the the), la correspondance d'une chaîne entourée de guillemets équilibrés ("..." ou '...' mais pas "...'), ou la vérification qu'une balise de type HTML est fermée par le même nom de balise. Aucun de ces cas n'est possible avec des motifs littéraux ordinaires, car le texte à faire correspondre n'est pas connu tant que l'expression régulière n'est pas exécutée.
Ce guide couvre les rétroréférences numérotées (\1, \2, …), les rétroréférences nommées (\k<name>), la manière dont les groupes sont numérotés, les pièges courants, et comment réutiliser le texte capturé dans String.prototype.replace().
Comment utiliser les rétroréférences
À l'intérieur d'un motif, une barre oblique inverse suivie d'un nombre fait référence au texte capturé par un groupe capturant. \1 correspond à ce que le groupe 1 a capturé, \2 au groupe 2, et ainsi de suite. Le point clé : cela correspond au texte capturé, et non au motif du groupe appliqué une nouvelle fois.
Ici (\w+) capture un mot dans le groupe 1, \s correspond à l'espace, et \1 exige le même mot à nouveau. Ainsi hello hello correspond, mais hello world ne correspond pas — \1 doit être égal à ce que le groupe 1 a capturé, et non simplement correspondre une deuxième fois à \w+.
Comment les groupes sont numérotés
Les numéros de groupe sont attribués par la position de la parenthèse ouvrante de chaque groupe, de gauche à droite. C'est important lorsque vous avez plusieurs groupes ou des groupes imbriqués :
La correspondance complète est le groupe 0 (m[0]), c'est pourquoi le premier groupe capturant est \1 et non \0. Pour les groupes imbriqués, le groupe externe obtient le numéro inférieur car son ( apparaît en premier.
Utiliser les groupes nommés
Les références numérotées deviennent difficiles à lire dès qu'un motif s'allonge. Vous pouvez à la place nommer un groupe avec (?<name>…) et le référencer avec \k<name>. Pour plus de détails sur la déclaration des groupes nommés, consultez Groupes capturants.
Ici (?<word>\w+) est un groupe nommé et \k<word> y fait référence. Après une correspondance réussie, le texte capturé est également disponible sur l'objet match.groups. Les groupes nommés et \k<name> fonctionnent dans tous les navigateurs modernes et dans les versions actuelles de Node.js sans aucun drapeau.
Réutiliser les captures dans replace()
L'utilisation la plus courante des rétroréférences au quotidien n'est pas à l'intérieur du motif — c'est dans la chaîne de remplacement de String.prototype.replace(). Là, vous référencez le texte capturé avec $1, $2, … (ou $<name> pour les groupes nommés).
Un exemple pratique consiste à réduire un mot accidentellement doublé à un seul :
Notez la distinction : \1 (barre oblique inverse) est utilisé à l'intérieur du motif, tandis que $1 (signe dollar) est utilisé dans la chaîne de remplacement. Les confondre est une source fréquente de bogues.
Piège : les groupes non participants
Une rétroréférence vers un groupe qui n'a pas participé à la correspondance se comporte de manière particulière. Si le groupe n'a jamais correspondu (par exemple, il se trouvait dans une alternative non utilisée), sa valeur capturée est undefined, et en JavaScript la rétroréférence correspond alors à la chaîne vide — elle réussit sans consommer quoi que ce soit.
C'est facile à négliger : vous pourriez vous attendre à ce que \1 échoue lorsque le groupe n'a pas correspondu, mais il correspond silencieusement à rien. Structurez soigneusement votre alternance si vous comptez sur le fait qu'un groupe a toujours capturé quelque chose.
Un vrai cas d'usage : les guillemets équilibrés
Un motif pratique qui nécessite une rétroréférence est la correspondance d'une chaîne entre guillemets où le guillemet fermant doit être le même caractère que le guillemet ouvrant — "..." et '...' sont valides, mais "...' ne l'est pas.
Le groupe (['"]) capture le caractère guillemet qui a ouvert la chaîne, et \1 force la fermeture à être exactement ce même caractère. Un simple ["'].*?["'] ne pourrait pas imposer cela — il correspondrait volontiers à "...'. C'est la différence entre un lookahead/lookbehind (qui ne fait qu'affirmer) et une rétroréférence (qui fait correspondre à nouveau le texte capturé).
Conclusion
Utilisez une rétroréférence chaque fois qu'une partie ultérieure de la correspondance doit être égale au texte capturé précédemment — mots doublés, guillemets équilibrés, balises au même nom, ou règles du type « les caractères adjacents doivent être différents ». Retenez les trois points essentiels :
- Les groupes numérotés sont comptés par leur
(ouvrante, en commençant à\1; la correspondance complète est le groupe 0. - Utilisez
\1/\k<name>à l'intérieur du motif, et$1/$<name>dansreplace(). - Un groupe non participant fait correspondre sa rétroréférence à la chaîne vide, alors faites attention à vos alternances.
Pour les bases, consultez Groupes capturants, Classes de caractères et Lookahead et lookbehind.