3 points par GN⁺ 2024-03-21 | 1 commentaires | Partager sur WhatsApp

Comprendre le comportement du caractère « $ » dans les expressions régulières Python

  • Lorsqu’on utilise le module re de Python, on considère généralement que ^ signifie « début de chaîne » et que $ signifie « fin de chaîne ».
  • Cependant, $ ne signifie pas toujours uniquement « fin de chaîne », et son comportement peut varier selon la plateforme.
  • En Python, lorsque le mode multiligne est désactivé, le caractère $ peut correspondre à la fin de la chaîne ou juste avant le caractère de saut de ligne final.

Différence entre la fin de chaîne et la correspondance avec un saut de ligne

  • Quand le mode multiligne est désactivé, en Python, il ne faut pas utiliser uniquement $ si l’on veut faire correspondre strictement la fin de la chaîne sans caractère de saut de ligne.
  • On peut utiliser \z et \Z pour faire correspondre la fin de la chaîne.
  • En Python, avec re.MULTILINE, $ correspond à la fin de la chaîne ainsi qu’à la fin de chaque ligne (juste avant le saut de ligne).

Comparaison du comportement des expressions régulières sur différentes plateformes

  • Un tableau comparant la correspondance de motifs sur cat\n selon plusieurs plateformes montre que, si l’on autorise une correspondance incluant le saut de ligne, l’utilisation de $ en mode multiligne se comporte de manière cohérente.
  • Si l’on veut une correspondance sans inclure le saut de ligne, il faut utiliser \z sur toutes les plateformes sauf Python et ECMAScript, où il faut respectivement utiliser \Z ou $ sans mode multiligne.

L’avis de GN⁺

  • Cet article peut alerter les développeurs qui utilisent les expressions régulières sur le comportement inattendu du caractère $ en Python.
  • Il souligne que les expressions régulières sont très puissantes pour le traitement de chaînes, mais qu’il faut rester prudent car leur comportement peut varier d’une plateforme à l’autre.
  • Les développeurs doivent connaître ces différences et effectuer des tests supplémentaires pour éviter les problèmes de compatibilité lors du développement d’applications multiplateformes.
  • Parmi les autres bibliothèques d’expressions régulières offrant des fonctionnalités similaires, on trouve java.util.regex de Java et System.Text.RegularExpressions de .NET ; là aussi, il faut comprendre les différences de comportement propres à chaque plateforme.
  • Lorsqu’on introduit une nouvelle syntaxe ou un nouveau comportement d’expression régulière, il faut prendre en compte la compatibilité avec le code existant, l’impact sur les performances et la courbe d’apprentissage au sein de l’équipe, puis évaluer soigneusement les bénéfices et les coûts de ces changements.

1 commentaires

 
GN⁺ 2024-03-21
Commentaires Hacker News
  • Les personnes familières des expressions régulières savent que ^ signifie « début de chaîne » et $ « fin de chaîne ». Mais personnellement, je les considère comme « début de ligne » et « fin de ligne ». Dans la plupart des cas, comme on traite le texte ligne par ligne, le résultat est le même, mais la manière de penser à ces opérateurs ne change pas. C’est probablement parce que j’ai découvert les expressions régulières via grep et que je pense surtout l’entrée en termes de « lignes ».

    • Les expressions régulières POSIX et celles de Python sont différentes. Il faut généralement se référer à la documentation de l’implémentation utilisée, car la syntaxe n’est pas universelle.
    • Selon le chapitre 9 de POSIX, les expressions régulières sont généralement liées au traitement de texte et opèrent sur des chaînes terminées par un NUL marquant la fin de chaîne. Certains utilitaires limitent le traitement à des lignes. $ peut correspondre à la fin d’une chaîne ou à la fin d’une ligne, selon la définition de l’utilitaire (ou du mode). La plupart des utilitaires courants (grep, sed, awk, Python, etc.) le traitent par défaut comme une fin de ligne.
    • Il n’existe pas de syntaxe d’expression régulière universelle et unique. Sans connaître le langage et les options utilisés, on ne peut ni lire ni écrire des regex de manière fiable.
  • C’est l’occasion parfaite de présenter Robert Elder. Il crée du contenu sur YouTube et sur son blog, il a une série sur les expressions régulières et il explore en profondeur les différences de comportement entre divers outils.

    • Son contenu le plus récent est aussi excellent : https://www.youtube.com/watch?v=ys7yUyyQA-Y
    • Il a beaucoup de contenus susceptibles d’intéresser les utilisateurs de HN, par exemple sur la réalité et les difficultés du conseil.
  • Les expressions régulières ont été l’une des premières choses que j’ai vraiment intériorisées en apprenant Perl. (Perl garde toujours une place chaleureuse dans mon cœur grâce au livre « Camel »)

    • Aujourd’hui, l’information la plus importante est de savoir que les implémentations diffèrent, et de prendre l’habitude d’aller chercher la documentation de référence pour ce sur quoi on travaille.
    • Par exemple, les regex d’Emacs utilisent une classe de caractères comme « \s_- » au lieu de « \w » (ou quelque chose de ce genre à l’écran sans la doc), mais Emacs dispose de la meilleure documentation et de la meilleure découvrabilité.
    • Certains utilitaires exigent d’échapper les parenthèses et d’autres non. Parfois ce comportement est configurable, parfois non.
    • J’ai traversé toutes les étapes de la confusion, de l’agacement et du déni, et maintenant je l’accepte simplement. Le concept est partout le même, mais les variantes changent.
  • J’entends déjà de mauvais responsables du recrutement ajouter « comment fait-on correspondre la fin d’une chaîne en expression régulière ? » à leur liste de questions du genre « Ha ! tu ne connais pas le piège ! ».

  • Il est étrange d’omettre Perl de la liste quand on parle d’expressions régulières.

    • Description de $ dans la documentation perlre : correspond à la fin de la chaîne (ou avant le caractère de nouvelle ligne en fin de chaîne ; ou, avec /m, avant chaque nouvelle ligne)
  • Raku (anciennement Perl 6) choisit ^ et $ pour représenter le début et la fin d’une chaîne, et introduit ^^ et $$ pour représenter le début et la fin d’une ligne. Un mode multiligne n’est ni disponible ni nécessaire.

    • L’un des avantages d’une refonte/réécriture complète est qu’on peut tirer les leçons du fait que le comportement précédent surprenait les gens.
  • Il y a des gens qui pensent que les expressions régulières sont standardisées ? Passer dans un nouveau contexte, c’est toujours un nouvel apprentissage.

  • Il y a une confusion entre chaîne et ligne. Une chaîne est une suite de caractères, et une ligne peut désigner deux choses différentes. Si on considère le caractère de nouvelle ligne comme un terminateur de ligne, alors une ligne est une suite de caractères non-retour à la ligne incluant le caractère de nouvelle ligne. Sans caractère de nouvelle ligne, ce n’est pas une ligne complète. C’est l’approche utilisée par POSIX. Si on considère le caractère de nouvelle ligne comme un séparateur de lignes, alors une ligne est une suite de caractères non-retour à la ligne. Dans les deux cas, le contenu de la ligne s’arrête avant le caractère de nouvelle ligne, soit parce qu’il termine la ligne, soit parce qu’il la sépare de la suivante.

    • Le sens de ^ et $ repose sur la notion de ligne, que l’on soit en mode monoligne ou multiligne. Pour une sémantique fondée sur la chaîne — quand on traite un fichier, on peut penser au fichier entier — on utilise \A et \Z ou leurs équivalents.
  • Cela a conduit à plusieurs bugs graves dans des applications basées sur Ruby. J’utilise toujours \A\z.