<!– @page { margin: 2cm } –>
Les ancres de début et de fin de chaîne
Jusque-là, je vous ai expliqué les caractères littéraux et les classes de caractères. Pour chacun d’eux, son ajout dans un regex force le moteur de regex à matcher un seul caractère.
Les ancres se comportent différemment. Elles ne matchent aucun caractère. Au lieu de cela, elles matchent une position avant, après ou à l’intérieur des caractères. Elles peuvent être utilisées pour “ancrer” le matche du regex à une position donnée. Le puissance matche la position avant le premier caractère dans la chaîne. Appliquer ^a sur abc matche a. ^b ne matche pas du tout abc, parce que le b n’est pas juste après le début de la chaîne, matché par ^. Voir ci-dessous le fonctionnement interne du moteur de regex.
De même, $ matche juste après le dernier caractère de la chaîne. C$ matche c dans abc, alors que a ne matche pas du tout.
Applications utiles
Lorsqu’on utilise des expressions régulières pour valider la saisie de l’utilisateur avec un langage de programmation, l’usage des ancres est très important. Si vous utilisez le code si($input =~ m/d+/) dans un script Perl pour voir si l’utilisateur a bien entré un nombre, il acceptera la saisie même si l’utilisateur a tapé qsdf4ghjk, car d+ matche le 4. Le bon regex est ^d+$. Comme “début de chaîne” doit être matché avant d et “fin de chaîne” doit être matché juste après, la chaîne entière doit être constituée de chiffres pour que d+$ puisse matcher.
Cela arrive couramment que l’utilisateur tape des espaces involontaires. Lorsque Perl traite une ligne dans un fichier texte, le saut de ligne sera également stocké dans la variable. Alors avant de valider la saisie, une bonne pratique est de couper les espaces avant et après la chaîne. ^s+ matche les espaces avant et s+$ matche les espaces après la chaîne. En Perl, vous pouvez utiliser $input =~ s/^s+|s+$//g. Une utilisation appropriée et /g permet de tout faire en une seule ligne de code.
Utiliser ^ et $ comme ancres de début et de fin de ligne
Si vous avez une chaîne consistués de lignes multiples, comme première lignenseconde ligne (où n indique un saut de ligne), il est souvent désirable de travailler avec des lignes, plutôt qu’avec la chaîne entière. D’ailleurs, tous les moteurs de regex présentés dans ce tutoriel ont l’occasion d’étendre le sens de chaque ancre. ^ peut alors matcher le début de la chaîne (avant le p dans la chaîne ci-dessus), ainsi qu’après chaque saut de ligne (entre n et s). De même, $ matchera toujours à la fin de la chaîne (après le dernier e), et aussi avant chaque saut de ligne (entre e et n).
Dans chaque éditeur comme EditPad Pro ou GNU Emacs, et les outils regex comme PowerGREP, le puissance et le dollard matchent toujours à la fin et au début de chaque ligne. C’est logique, car ces éditeurs sont faits pour travailler sur des fichiers entiers, pas sur des chaînes courtes.
Dans tous les languages de programmation, à l’exception de Ruby, vous devez explicitement activer cette fonction. Elle est communément appelée le “mode multi lignes”. Vous pouvez activer le mode ligne simple en ajoutant u s après le code de regex, comme ceci : m/^regex$m ;. Dans .NET, l’ancre matche avant et après chaque retour à la ligne lorsque vous spécifiez RegexOptions.Multiline, comme dans Regex.Match(”string”,”regex”, RegexOptions.Multiline).
Les ancres permanentes de début et de fin de chaîne
A ne matche qu’en début de chaîne. De même, Z ne matche qu’en fin de chaîne. Ces deux éléments ne matchent jamais les sauts de ligne. Cela reste vrai dans tous les styles de regex discutés dans ce tutoriel, même avec le “mode multiligne” activé. Dans EditPad Pro et PowerGREP, où le puissance et le dollard matchent toujours à la fin et au début de chaque ligne, A et Z matchent seulement au début et à la fin du fichier entier.
Javascript, POSIX et XML ne supportent pas A et Z. Vous êtes obligé d’utiliser le puissance et le dollar pour cet objectif.
Les extentions GNU des expressions régulières POSIX utilisent `(backtick) pour matcher le début de la chaîne et ‘ (apostrophe) pour matcher la fin de la chaîne.
Matches vides
Nous avons vu que les ancres matchent une position, plus qu’un caractère. Cela signifie que lorsqu’un regex est constitué d’une ancre ou plus, il peut finir en un match vide. Selon la situation, cela peut être très utile ou très mauvais. Utiliser ^d*$ pour tester si l’utilisateur a entré un nombre (notez l’usage du * à la place du +), force le script à accepter une chaîne vide comme une entrée correcte. Voir ci-dessous.
Cependant, matcher seulement une position peut être très pratique. Dans les emails, par exemple, il est commun d’utiliser un symmbole “plus grand que” et un espacement à chaque ligne du message précédent. En VB.NET, on peut le faire facilement avec DIM Quoted as String = Regex.Replace(Original, “^”, “> “, RegexOptions.Multilines). On utilise le mode multilignes, pour que le regex ^ matche le début du message précédent, et après chaque retour à la ligne. La méthode Regex.Replace va enlever le match regex de la chaîne, et insérer la chaîne de remplacement (symbole plus grand que et espacement). Comme le matche n’inclut aucun caractère, on n’efface rien. Cependant, le matche comprend une position de départ, et la chaîne de remplacement est ajoutée là, comme on le voulait.
Les chaînes qui finissent par un saut de ligne
Bien que Z et $ ne matchent qu’en fin de chaîne (lorsque l’option pour que le puissance et le dollard matchent les sauts de ligne dans la chaîne est désactivée), il existe une exception. Si la chaîne finit avec un saut de ligne, alors Z et $ matcheront la position juste avant ce dernier, plutôt qu’à la fin de la chaîne. Cette “amélioration” a été introduite par Perl, et elle est copiée par de nombreux styles de regex tels que Java, .NET, et PCRE. En Perl, quand on lit une ligne dans un fichier, la chaîne lue se finira par un saut de ligne. Si on lit une ligne d’un fichier contenant le texte “joe” on obtient la chaîne joen. Lorsqu’on les applique à cette chaîne, ^[a-z]+$ et A[a-z]+Z matcheront tous deux joe.
Si vous voulez un matche à la fin véritable de la chaîne, utilisez z (un z en bas de casse plutôt qu’en haut de casse). A[a-z]+z ne matche pas joen. z matche après le saut de ligne, et celui-ci n’est pas supporté par la classe de caractères.
Un regard sur le moteur de regex
Voyons ce qui peut arriver quand on essaie de matcher ^4$ à 749n486n4 (où n correspond à un caaractère de saut de ligne) en mode multiligne. Comme d’habitude, le moteur commence au premier caractère : 7.Le premier élément dans l’expression régulière est ^. Comme c’est un élément de taille zéro, le moteur n’essaie pas de le matcher à un caractère, mais plutôt avec la position avant le caractère que le moteur a atteint. ^ matche effectivement la position avant le 7. Le moteur avance ensuite à l’élément suivant : 4. 4. Comme l’élément précédent avait une taille zéro, le moteur de regex n’avance pas au caractère suivant dans la chaîne. Il reste à 7. 4 est un caractère littéral, qui ne matche pas 7. Il n’y a pas d’autres permutations du regex, donc le moteur recommence avec le premier élément du regex, avec le caractère suivant. 4. Cette fois, ^ ne matche pas la position avant le 4. Cette position est précédée par un caractère, et ce n’est pas un saut de ligne. Le moteur continue avec 9 et échoue encore. La tentative suivant en n, échoue également. De nouveau, la position avant le n est précédée par un caractère, 9, et ce n’est pas un saut de ligne.
Ensuite, le moteur regex arrive au second 4 de la chaîne. Le ^ matche la position avant le 4, car elle est précédée d’un caractère de saut de ligne. De nouveau, le moteur regex avance à l’élément suivant, 4,mais il n’avance pas au caractère suivant dans la chaîne. 4 matche 4, et le moteur regex avance l’élément regex et le caractère de la chaîne. Alors le moteur tente de matcher $ avec la position avant (eh oui : avant) le 8.Le $ ne peut pas matcher ici, car cette position est suivie d’un caractère, et ce caractère n’est pas un saut de ligne.
Encore une fois, le moteur doit tenter de matcher de nouveau le premier élément. Précédemment, il avait matché le second 4, donc le moteur continue au prchain caractère, 8, et le ^ ne matche pas. Pareil pour le 6 et la nouvelle ligne.
Finallement, le regex tente de matcher le premier élément avec le troisième 4 de la chaîne. Cela fonctionne. Ensuite, le moteur matche 4 et 4. L’élément de regex courant est avancé à $, et le caractère courant est avancé à la toute dernière position dans la chaîne : le vide après la chaîne. Aucun élément de regex qui a besoin d’un élément pour matcher ne peut matcher ici. Même une classe de caractères négationnée. Cependant, on essaie de matcher un dollard, et ce dollard est un drôle d’animal. Il a une largeur de zéro, donc il essaiera de matcher la position avant le caractère courant. Cela ne change rien que ce “caractère” soit le vide après la chaîne. En faite, le dollard va matcher le caractère courant. Ce doit être soit un saut de ligne, soit le vide après la chaîne pour que le $ matche la position avant le caractère courant. Comme c’est le cas dans l’exemple, le dollard matche correctement.
Comme $ était le dernier élément de la chaîne, le moteur a trouvé un matche : le dernier 4 dans la chaîne.
Un autre regard
Plus tôt j’ai mentionné que ^d*$ pouvait matcher une chaîne vide. Voyons comment.
Il n’y a qu’une position de “caractère” dans une chaîne vide : le vide après la chaîne.Le premier élément du regex est ^. Il matche la position avant le vide après la chaîne, car il est précédé par le vide avant la chaîne. L’élément suivant est d*. Comme nous verrons plus tard, un des effets de * est que dans ce cas, il rend le d optionnel. Le moteur tentera de matcher le d avec le vide après la chaîne. Cela échoue, mais le * change l’échec de d en un succès de taille zéro. Le moteur va alors avancer vers l’élément suivant, sans avancer la position dans la chaîne. Le moteur arrive ainsi au $, et au vide après la chaîne. Nous avons déjà vu que ces deux-là matchent. Arrivé là, le regex matche la chaîne vide, et le moteur rapporte un succès.
Note pour les programmeurs
Une expression régulière comme un $ seul peut très bien matcher la chaîne. Si vous interrogez alors le moteur sur la position du caractère, il retournerait la longueur de la chaîne si l’indice des chaînes part de zéro, ou la longueur +1 si l’indice de la chaîne part de de un suivant votre langage de programmation. Si vous interrogez le moteur sur la longueur du matche, il retournera zéro.
Ce dont vous devez vous méfier, c’est que String[Regex.MatchPosition] peut causer une violation d’accès ou une faute d’erreur de segmentation, car MatchPosition peut pointer sur le vide après la chaîne. Cela peut aussi arriver avec ^ et ^$ si le dernier caractère de la chaîne est un saut de ligne.

