Les suggestions de cet article se concentrent principalement sur la lisibilité des expressions régulières. En développant ces habitudes pendant le développement, vous considérerez plus clairement la conception et la structure de l'expression, ce qui contribuera à réduire les bogues et la maintenance du code. êtes vous-même le responsable de ce code. Vous pouvez jeter un œil par vous-même et prêter attention à ces expériences avec les expressions régulières dans votre utilisation réelle.
Les expressions régulières sont difficiles à écrire, à lire et à maintenir. Elles ne correspondent souvent pas à du texte inattendu ou manquent de texte valide. Ces problèmes sont causés par les performances et les capacités des expressions régulières. La combinaison des capacités et des nuances de chaque métacaractère rend le code impossible à interpréter sans recourir à des astuces intellectuelles.
De nombreux outils incluent des fonctionnalités qui facilitent la lecture et l'écriture d'expressions régulières, mais ils sont également très non idiomatiques. Pour de nombreux programmeurs, écrire des expressions régulières est un art magique. Ils s’en tiennent aux caractéristiques qu’ils connaissent et ont une attitude d’optimisme absolu. Si vous êtes prêt à adopter les cinq habitudes décrites dans cet article, vous serez en mesure de concevoir des expressions régulières qui résistent aux essais et aux erreurs.
Cet article utilisera les langages Perl, PHP et Python comme exemples de code, mais les conseils de cet article s'appliquent à presque toutes les implémentations d'expressions de remplacement (regex).
1. Utilisez des espaces et des commentaires.
Pour la plupart des programmeurs, l'utilisation d'espaces et d'indentations dans un environnement d'expression régulière n'est pas un problème s'ils ne le font pas, leurs pairs et même les profanes se moqueront certainement d'eux. Presque tout le monde sait que regrouper du code sur une seule ligne rend difficile la lecture, l’écriture et la maintenance. Quelle est la différence pour les expressions régulières ?
La plupart des outils d'expression de remplacement disposent d'une fonctionnalité d'espaces étendus, qui permet aux programmeurs d'étendre leurs expressions régulières sur plusieurs lignes et d'ajouter des commentaires à la fin de chaque ligne. Pourquoi seul un petit nombre de programmeurs profitent-ils de cette fonctionnalité ? Les expressions régulières de Perl 6 utilisent par défaut des modèles étendus en espace. Ne laissez pas le langage agrandir les espaces par défaut pour vous, profitez-en vous-même.
Une astuce à retenir concernant les espaces étendus est de dire au moteur d’expression régulière d’ignorer les espaces étendus. De cette façon, si vous devez faire correspondre des espaces, vous devez le spécifier explicitement.
En langage Perl, ajoutez x à la fin de l'expression régulière, ainsi "m/foo bar/" prend la forme suivante :
m/
foo
bar
/x
En langage PHP, ajoutez x à la fin de l'expression régulière, ainsi ""/foo bar/"" devient la forme suivante :
"/
foo
bar
/x"
Dans le langage Python, passez le paramètre de modification de motif "re.VERBOSE" pour obtenir la fonction compilée comme suit :
pattern = r'''
foo
bar
'''
regex = re.compile(pattern, re.VERBOSE)
gère des expressions régulières plus complexes, les espaces et les commentaires deviendront plus importants. Supposons que l'expression régulière suivante soit utilisée pour rechercher des numéros de téléphone aux États-Unis :
(?d{3})?d{3}[-.]d{4}
Cette expression régulière correspond à des numéros de téléphone tels que "( 314)555-4000", pensez-vous que cette expression régulière correspond à "314-555-4000" ou "555-4000" ? La réponse est qu’aucun des deux ne correspond. L'écriture d'une telle ligne de code masque les défauts et les résultats de la conception elle-même. L'indicatif régional téléphonique est requis, mais l'expression régulière n'a pas de symbole séparateur entre l'indicatif régional et le préfixe.
Diviser cette ligne de code en plusieurs lignes et ajouter des commentaires exposera les lacunes et facilitera la modification.
En langage Perl, il doit se présenter sous la forme suivante :
/
(? # parenthèses facultatives
d{3} # Indicatif régional téléphonique requis
)? # parenthèses facultatives
[-s.]? # Le délimiteur peut être un tiret, un espace ou un point
d{3} # Préfixe à trois chiffres
[-.] # Un autre délimiteur
d{4} # Numéro de téléphone à quatre chiffres
/x
L'expression régulière réécrite a désormais un séparateur facultatif après l'indicatif régional, de sorte qu'il doit correspondre à "314-555-4000", mais l'indicatif régional est toujours requis. Un autre programmeur qui souhaite rendre l'indicatif régional du téléphone facultatif peut rapidement constater qu'il ne l'est plus et qu'un petit changement peut résoudre le problème.
2.
Il existe trois niveaux de tests lors de l'écriture de tests. Chaque niveau ajoute une couche de fiabilité à votre code. Tout d’abord, vous devez réfléchir attentivement aux codes que vous devez faire correspondre et si vous pouvez gérer les incohérences. Deuxièmement, vous devez utiliser des instances de données pour tester l'expression régulière. Enfin, vous devez passer formellement un panel de test.
Décider quoi faire correspondre consiste en fait à trouver un équilibre entre faire correspondre les mauvais résultats et manquer les bons résultats. Si votre expression régulière est trop stricte, certaines correspondances correctes lui manqueront ; si elle est trop lâche, elle produira une correspondance incorrecte. Une fois qu'une expression régulière est intégrée au code réel, vous ne remarquerez peut-être pas les deux. Prenons l'exemple de numéro de téléphone ci-dessus, qui correspondrait à « 800-555-4000 = -5355 ». Les mauvaises correspondances sont en fait difficiles à détecter, il est donc important de planifier à l'avance et de bien les tester.
En continuant avec l'exemple du numéro de téléphone, si vous confirmez un numéro de téléphone dans un formulaire Web, vous pouvez vous contenter d'un numéro à dix chiffres dans n'importe quel format. Toutefois, si vous souhaitez séparer les numéros de téléphone d'une grande quantité de texte, vous devrez peut-être exclure soigneusement les fausses correspondances qui ne répondent pas aux exigences.
Lorsque vous réfléchissez aux données que vous souhaitez faire correspondre, notez quelques scénarios de cas. Écrivez du code pour tester votre expression régulière par rapport à un scénario de cas. Pour toute expression régulière complexe, il est préférable d’écrire un petit programme pour la tester, qui peut prendre la forme spécifique suivante.
En langage Perl :
#!/usr/bin/perl
my @tests = ( "314-555-4000",
"800-555-4400",
"(314)555-4000",
"314.555.4000",
"555-4000",
"aasdklfjklas",
"1234-123-12345"
);
foreach mon $test (@tests) {
si ( $test =~ m/
(? # parenthèses facultatives
d{3} # Indicatif régional téléphonique requis
)? # parenthèses facultatives
[-s.]? # Le délimiteur peut être un tiret, un espace ou un point
d{3} # Préfixe à trois chiffres
[-s.] # Un autre délimiteur
d{4} # Numéro de téléphone à quatre chiffres
/x ) {
print "Correspondance sur $testn" ;
}
autre {
print "Échec de la correspondance sur $testn" ;
}
}
En langage PHP :
<?php
$tests = tableau( "314-555-4000",
"800-555-4400",
"(314)555-4000",
"314.555.4000",
"555-4000",
"aasdklfjklas",
"1234-123-12345" );
$regex = "/
(? # parenthèses facultatives
d{3} # Indicatif régional téléphonique requis
)? # parenthèses facultatives
[-s.]? # Le délimiteur peut être un tiret, un espace ou un point
d{3} # Préfixe à trois chiffres
[-s.] # Un autre délimiteur
d{4} # Numéro de téléphone à quatre chiffres
/x";
foreach ($tests comme $test) {
if (preg_match($regex, $test)) {
echo "Correspondance sur $test
;";
}
autre {
echo "Échec de la correspondance sur $test
;";
}
}
?>;
En langage Python :
import re
tests = ["314-555-4000",
"800-555-4400",
"(314)555-4000",
"314.555.4000",
"555-4000",
"aasdklfjklas",
"1234-123-12345"
]
motif = r'''
(? # parenthèses facultatives
d{3} # Indicatif régional téléphonique requis
)? # parenthèses facultatives
[-s.]? # Le délimiteur peut être un tiret, un espace ou un point
d{3} # Préfixe à trois chiffres
[-s.] # Un autre délimiteur
d{4} # Numéro de téléphone à quatre chiffres
'''
regex = re.compile( pattern, re.VERBOSE ) pour test dans les tests :
si regex.match(test):
imprimer "Correspondance", test, "n"
autre:
print "Failed match on", test, "n"
L'exécution du code de test révélera un autre problème : il correspond à "1234-123-12345".
En théorie, vous devez intégrer tous les tests de l’ensemble de l’application dans une équipe de tests. Même si vous n'avez pas encore de groupe de tests, vos tests d'expressions régulières constitueront une bonne base pour en créer un, et c'est le bon moment pour en créer un. Même si ce n'est pas encore le bon moment pour la créer, vous devez quand même exécuter et tester l'expression régulière après chaque modification. Passer un peu de temps ici vous évitera bien des ennuis.
3. Groupe d'opérations alternées
Le symbole d'opération alternée ( ) a une faible priorité, ce qui signifie qu'il alterne souvent plus que ce que le programmateur avait prévu. Par exemple, l'expression régulière permettant d'extraire les adresses e-mail du texte pourrait être la suivante :
^CC: To:(.*)
La tentative ci-dessus est incorrecte, mais ce bug est souvent ignoré. Le but du code ci-dessus est de retrouver le texte commençant par « CC : » ou « To : » puis d'extraire l'adresse email à la fin de cette ligne.
Malheureusement, si « To : » apparaît au milieu d'une ligne, cette expression régulière ne capturera aucune ligne commençant par « CC : » et extraira plutôt plusieurs morceaux de texte aléatoires. Franchement, l'expression régulière correspond à une ligne commençant par « CC : » mais ne capture rien ; ou elle correspond à n'importe quelle ligne contenant « To : » mais capture le reste de la ligne. Normalement, cette expression régulière capturerait un grand nombre d’adresses e-mail, donc personne ne remarquerait le bug.
Si vous souhaitez répondre à l'intention réelle, vous devez ajouter des parenthèses pour que ce soit clair. L'expression régulière est la suivante :
(^CC:) (To:(.*))
Si l'intention réelle est de capturer du texte commençant par " CC:" ou "To:" le reste de la ligne, alors l'expression régulière correcte est :
^(CC: To:)(.*)
Il s'agit d'un bug de correspondance incomplète courant que vous éviterez si vous prenez l'habitude de regrouper pour les opérations alternées Cette erreur.
4. Utilisez des quantificateurs lâches
De nombreux programmeurs évitent d'utiliser des quantificateurs lâches tels que "*?", "+?" et "??", même s'ils rendent l'expression plus facile à écrire et à comprendre.
Les quantificateurs détendus correspondent au moins de texte possible, ce qui permet d'obtenir une correspondance exacte. Si vous écriviez "foo(.*?)bar", le quantificateur cesserait de correspondre la première fois qu'il rencontrerait "bar", pas la dernière fois. Ceci est important si vous souhaitez capturer "###" à partir de "foo###bar+++bar". Un quantificateur strict capturerait "###bar++ +". ;), cela va causer beaucoup de problèmes. Si vous utilisez des quantificateurs détendus, vous pouvez générer de nouvelles expressions régulières en passant très peu de temps à assembler les types de caractères.
Les quantificateurs détendus sont d'une grande valeur lorsque vous connaissez la structure du contexte dans lequel vous souhaitez capturer le texte.
5. Utilisez les délimiteurs disponibles.
Les langages Perl et PHP utilisent souvent une barre oblique gauche (/) pour marquer le début et la fin d'une expression régulière. Le langage Python utilise un ensemble de guillemets pour marquer le début et la fin. Si vous insistez pour utiliser des barres obliques gauches en Perl et PHP, vous souhaiterez éviter les barres obliques dans les expressions ; si vous utilisez des guillemets en Python, vous souhaiterez éviter les barres obliques inverses (). Choisir différents délimiteurs ou guillemets peut vous permettre d'éviter la moitié de l'expression régulière. Cela rendra les expressions plus faciles à lire et réduira les bugs potentiels causés par l’oubli d’éviter les symboles.
Les langages Perl et PHP permettent d'utiliser tous les caractères non numériques et espaces comme délimiteurs. Si vous passez à un nouveau délimiteur, vous pouvez éviter de manquer la barre oblique gauche lors de la correspondance des URL ou des balises HTML (telles que "http://" ou "<br/>;").
Par exemple, "/http://(S)*/" peut être écrit sous la forme "#http://(S)*#".
Les délimiteurs courants sont "#", "!" et " ". Si vous utilisez des crochets, des crochets angulaires ou des accolades, faites-les correspondre. Voici quelques exemples de délimiteurs courants :
#…# !…! {…} s … … (Perl uniquement) s[…][…] (Perl uniquement) s<…>;/…/ (Perl uniquement)
En Python, une expression régulière est d'abord traitée comme une chaîne. Si vous utilisez des guillemets comme délimiteurs, vous manquerez toutes les barres obliques inverses. Mais vous pouvez éviter ce problème en utilisant la chaîne "r". Si vous utilisez trois guillemets simples consécutifs pour l'option "re.VERBOSE", cela vous permettra d'inclure des nouvelles lignes. Par exemple, regex = "( file://w+)(//d +)" peut être écrit sous la forme suivante :
regex = r'''
(w+)
(d+)
'''