Expressions régulières avec Go Partie 2

Vue d'ensemble

Ceci est la deuxième partie d'une série de tutoriels en deux parties sur les expressions régulières dans Go. Dans la première partie, nous avons appris ce que sont les expressions régulières, comment les exprimer dans Go et les bases de l’utilisation de la bibliothèque Go regexp pour faire correspondre le texte à des modèles d’expression régulière.. 

Dans la deuxième partie, nous allons nous concentrer sur l'utilisation de la bibliothèque regexp dans toute son étendue, y compris la compilation d'expressions régulières, la recherche d'une ou plusieurs correspondances dans le texte, le remplacement d'expressions régulières, le regroupement des sous-correspondances et le traitement de nouvelles lignes..

Utilisation de la bibliothèque Regexp

La bibliothèque regexp fournit un support à part entière pour les expressions régulières ainsi que la possibilité de compiler vos modèles pour une exécution plus efficace lorsque vous utilisez le même modèle pour faire correspondre plusieurs textes. Vous pouvez également rechercher des index de correspondances, remplacer des correspondances et utiliser des groupes. Plongeons dedans.

Compiler votre regex

Il y a deux méthodes pour compiler des expressions rationnelles: Compiler() et MustCompile (). Compiler() retournera une erreur si le modèle fourni est invalide. MustCompile () va paniquer. La compilation est recommandée si vous vous souciez de la performance et prévoyez d’utiliser plusieurs fois la même expression régulière. Changeons notre rencontre() fonction d'assistance pour prendre une expression rationnelle compilée. Notez qu'il n'est pas nécessaire de rechercher des erreurs car l'expression rationnelle compilée doit être valide.

func match (r * regexp.Regexp, chaîne de texte) apparié: = r.MatchString (texte) s'il a été mis en correspondance fmt.Println ("√", r.String (), ":", texte) else fmt. Println ("X", r.String (), ":", text) 

Voici comment compiler et utiliser plusieurs fois la même expression rationnelle compilée:

func main () es: = '(\ bcats? \ b) | (\ bdogs? \ b) | (\ brats? \ b)' e: = regexp.MustCompile (es) match (e, "Il pleut des chiens and cats ") match (e," Le catalogue est prêt. C'est l'heure du hot-dog! ") match (e," C'est un monde qui mange le chien. ") Résultat: √ (\ bcats? \ b) | (\ bdogs? \ b) | (\ marmots? \ b): Il pleut des chiens et des chats X (\ bcats? \ b) | (\ bdogs? \ b) | (\ marmots? \ b): Le catalogue est prêt. C'est l'heure du hot-dog! √ (\ bcats? \ B) | (\ bdogs? \ B) | (\ marmots? \ B): C'est un monde de chien qui mange. 

Découverte

L'objet Regexp a un lot de FindXXX () méthodes. Certains renvoient la première correspondance, d'autres renvoient toutes les correspondances et d'autres renvoient un ou plusieurs index. Fait intéressant, les noms des 16 méthodes de fonctions correspondent aux expressions rationnelles suivantes: Rechercher (Tout)? (Chaîne)? (Soumettre)? (Index)?

Si "Tous" est présent, toutes les correspondances sont renvoyées par rapport à la plus à gauche. Si 'String' est présent, le texte cible et les valeurs de retour sont des chaînes contre des tableaux d'octets. Si "Submatch" est présent, les sous-matchs (groupes) sont renvoyés par rapport à de simples correspondances. Si 'Index' est présent, les index dans le texte cible sont renvoyés par rapport aux correspondances réelles.

Reprenons l’une des fonctions les plus complexes et utilisons le FindAllStringSubmatch () méthode. Il faut une ficelle et un nombre n. Si n est -1, il retournera tous les index correspondants. Si n est un entier non négatif, il retournera les n correspondances les plus à gauche. Le résultat est une tranche de ficelle. 

Le résultat de chaque sous-correspondance est la correspondance complète suivie du groupe capturé. Par exemple, considérons une liste de noms où certains d'entre eux ont des titres tels que "M.", "Mme" ou "Dr.". Voici une regex qui capture le titre en tant que sous-correspondance, puis le reste du nom après un espace: \ b (Mr \. | Mrs \. | Dr \.). *.

func main () re: = regexp.MustCompile ('\ b (Mr \. | Mrs \. | Dr \.). *') fmt.Println (re.FindAllStringSubmatch ("Dr. Dolittle", -1)) fmt.Println (re.FindAllStringSubmatch ('Mme. Doubtfire M. Anderson', -1)) Résultat: [[Dr. Dolittle Dr.]] [[Mme Doubtfire Mme] [M. Anderson M.]] 

Comme vous pouvez le voir dans le résultat, l'intégralité de la correspondance est capturée en premier, suivie du titre. Pour chaque ligne, la recherche est réinitialisée.

Remplacement

Trouver des correspondances est une bonne chose, mais souvent vous devrez peut-être remplacer la correspondance par autre chose. L'objet Regexp a plusieurs Remplacer XXX () les méthodes habituelles pour traiter les chaînes de caractères vs les tableaux d'octets et les remplacements littéraux vs les extensions. Dans le grand livre 1984 de George Orwell, les slogans du parti sont inscrits sur la pyramide blanche du ministère de la vérité: 

  • La guerre est la paix 
  • La liberté est l'esclavage 
  • L'ignorance est une force 

J'ai trouvé un petit essai sur le prix de la liberté qui utilise certains de ces termes. Corrigeons un extrait de celui-ci en fonction de la parole du parti en utilisant Go regexes. Notez que certains mots cibles pour le remplacement utilisent une casse différente. La solution consiste à ajouter le drapeau insensible à la casse (je?) au début de la regex. 

Comme la traduction est différente selon les cas, nous avons besoin d’une approche plus sophistiquée que du remplacement littéral. Heureusement (ou par conception), l'objet Regexp a une méthode de remplacement qui accepte une fonction qu'il utilise pour effectuer le remplacement réel. Définissons notre fonction replacer qui retourne la traduction avec la casse correcte.

fonction de remplacement (chaîne de caractères) chaîne d: = map [chaîne] chaîne "war": "paix", "WAR": "PEACE", "guerre": "paix", "liberté": "esclavage", " LIBERTÉ ":" ESCLAVAGE "," LIBERTÉ ":" Esclavage "," ignorance ":" force "," IGNORANCE ":" FORCE "," Ignorance ":" Force ", r, ok: = d [s] if ok return r else return s 

Maintenant, nous pouvons effectuer le remplacement réel:

func main () text: = LE PRIX DE LA LIBERTE: les Américains en guerre Les Américains sont entrés en guerre pour conquérir leur indépendance, élargir leurs frontières nationales, définir leurs libertés et défendre leurs intérêts dans le monde entier. ' expr: = '(? i) (war | freedom | ignorance)' r: = regexp.MustCompile (expr) result: = r.ReplaceAllStringFunc (text, replacer) fmt.Println (result) Résultat: LE PRIX DE L'ESCLAVAGE: Des Américains en paix Des Américains sont allés en paix pour obtenir leur indépendance, élargir leurs frontières nationales, définir leur esclavage et défendre leurs intérêts dans le monde entier.. 

La sortie est quelque peu incohérente, ce qui est la marque d'une bonne propagande.

Regroupement

Nous avons déjà vu comment utiliser le regroupement avec des sous-correspondances. Mais il est parfois difficile de gérer plusieurs sous-correspondances. Les groupes nommés peuvent beaucoup aider ici. Voici comment nommer vos groupes de sous-correspondances et remplir un dictionnaire pour un accès facile par nom:

func main () e: = '(? P\ w +) (? P.+ )? (? P\ w +) 'r: = regexp.MustCompile (e) noms: = r.SubexpNames () fullNames: = [] chaîne ' John F. Kennedy ',' Michael Jordan ' pour _, nom complet: = plage fullNames result : = r.FindAllStringSubmatch (nom complet, -1) m: = carte [chaîne] chaîne  pour i, n: = résultat de la plage [0] m [noms [i]] = n fmt.Println ("prénom : ", m [" premier "]) fmt.Println (" prénom du milieu: ", m [" milieu "]) fmt.Println (" nom de famille: ", m [" dernier "]) fmt.Println () Sortie: prénom: John prénom du prénom: F. nom de famille: Kennedy prénom: Michael prénom du prénom: nom de famille: Jordan

Faire face à de nouvelles lignes

Si vous vous en souvenez, j'ai dit que le caractère spécial point correspond à n'importe quel caractère. Eh bien, j'ai menti. Cela ne correspond pas à la nouvelle ligne (\ n) caractère par défaut. Cela signifie que vos correspondances ne se croiseront pas à moins que vous ne le spécifiiez explicitement avec l'indicateur spécial. (? s) que vous pouvez ajouter au début de votre regex. Voici un exemple avec et sans le drapeau.

func main () text: = "1111 \ n2222" expr: = [] chaîne ". *", "(? s). *" pour _, e: = intervalle expr r: = regexp.MustCompile ( e) result: = r.FindString (text) result = strings.Replace (result, "\ n", '\ n', -1) fmt.Println (e, ":", result) fmt.Println ()  Sortie:. *: 1111 (? S). *: 1111 \ n2222 

Une autre considération est de savoir s'il faut traiter les ^ et $ caractères spéciaux comme le début et la fin de tout le texte (par défaut) ou comme le début et la fin de chaque ligne avec le (? m) drapeau.  

Conclusion

Les expressions régulières sont un outil puissant lorsque vous travaillez avec du texte semi-structuré. Vous pouvez les utiliser pour valider une saisie de texte, la nettoyer, la transformer, la normaliser et, de manière générale, gérer une grande diversité en utilisant une syntaxe concise.. 

Go fournit une bibliothèque avec une interface conviviale composée d'un objet Regexp avec de nombreuses méthodes. Essayez, mais méfiez-vous des pièges.