Expressions régulières avec Go Partie 1

Vue d'ensemble

Les expressions régulières (AKA regex) sont un langage formel qui définit une séquence de caractères avec un motif. Dans le monde réel, ils peuvent être utilisés pour résoudre de nombreux problèmes avec un texte semi-structuré. Vous pouvez extraire les éléments importants du texte avec beaucoup de décorations ou un contenu sans rapport. Go a un paquet regex fort dans sa bibliothèque standard qui vous permet de couper du texte avec des regex. 

Dans cette série en deux parties, vous apprendrez ce que sont les expressions régulières et comment les utiliser efficacement dans Accomplir de nombreuses tâches courantes. Si vous n'êtes pas du tout familiarisé avec les expressions régulières, il existe de très bons tutoriels. En voici un bon.

Comprendre les expressions régulières

Commençons par un exemple rapide. Vous avez du texte et vous voulez vérifier s'il contient une adresse e-mail. Une adresse e-mail est spécifiée de manière rigoureuse dans la RFC 822. En bref, elle comporte une partie locale suivie d'un symbole @ suivi d'un domaine. L'adresse mail sera séparée du reste du texte par un espace. 

Pour déterminer si elle contient une adresse e-mail, la regex suivante fera l'affaire: ^ \ w + @ \ w + \. \ w + $. Notez que cette expression rationnelle est un peu permissive et autorisera certaines adresses électroniques non valides. Mais il suffit de démontrer le concept. Essayons-le sur quelques adresses e-mail potentielles avant d'expliquer son fonctionnement:

package main import ("os" "regexp" "fmt") func check (erreur err) if err! = nil fmt.Println (err.Error ()) os.Exit (1) func main ()  emails: = [] chaîne "brown @ fox", "brown @ fox.", "[email protected]", "br @ own @ fox.com", modèle: = '^ \ w + @ \ w + \ . \ w + $ 'pour _, email: = plage d'e-mails appariés, erreur: = regexp.Match (modèle, [] octet (email)), cochez (err) si fmt.Printf ("√'% s 'est un e-mail valide \ n ", e-mail) autre fmt.Printf (" X '% s' n'est pas un e-mail valide \ n ", e-mail) Sortie: X 'brown @ fox' n'est pas un e-mail valide X «Brown @ Fox». n'est pas un email valide √ '[email protected]' est un email valide X 'br @ own @ fox.com' n'est pas un email valide

Notre expression régulière fonctionne sur ce petit échantillon. Les deux premières adresses ont été rejetées car le domaine ne comportait aucun point ou aucun caractère après le point. Le troisième email a été formaté correctement. Le dernier candidat avait deux symboles @.

Brisons cette regex vers le bas: ^ \ w + @ \ w + \. \ w + $

Caractère / Symbole Sens
^ Début du texte cible
\ w Tous les caractères du mot [0-9A-Za-z_]
+ Au moins un des personnages précédents
@ Littéralement le caractère @ 
\. Le caractère littéral de point. Doit être échappé avec \
$ Fin du texte cible

Au total, cette expression rationnelle correspondra aux morceaux de texte commençant par un ou plusieurs caractères de mot, suivis du caractère "@", suivis de nouveau par un ou plusieurs caractères de mot, suivis d'un point et suivis par un ou plusieurs caractères.  

Traiter avec des personnages spéciaux

Les caractères suivants ont une signification particulière dans les expressions régulières: .+*? () | [] ^ $ \. Nous en avons déjà vu beaucoup dans l'exemple de courrier électronique. Si nous voulons les faire correspondre à la lettre, nous devons leur échapper avec une barre oblique inverse. Introduisons une petite fonction d'assistance appelée rencontre() cela nous évitera beaucoup de dactylographie. Il faut un motif et du texte, utilise le regexp.Match () méthode pour faire correspondre le modèle au texte (après conversion du texte en un tableau d'octets), et affiche les résultats:

func match (chaîne de modèle, chaîne de texte) apparié, _: = regexp.Match (modèle, [] octet (texte)) s'il y a correspondance fmt.Println ("√", modèle, ":", texte) else  fmt.Println ("X", motif, ":", texte)

Voici un exemple de correspondance d'un personnage ordinaire comme z faire correspondre un caractère spécial comme ?:

func main () text: = "Puis-je jouer au cheezburger?" motif: = "z" correspondance (motif, texte) motif = "\\?" match (motif, texte) motif = '\?' match (motif, texte) Sortie: √ z: Puis-je haz cheezburger? √ \? : Puis-je haz cheezburger? √ \? : Puis-je haz cheezburger? 

Le motif regex \? contient une barre oblique inversée qui doit être échappée avec une autre barre oblique inversée lorsqu'elle est représentée sous la forme d'une chaîne normale. La raison en est que la barre oblique inversée est également utilisée pour échapper des caractères spéciaux dans les chaînes de caractères Go, comme newline (\ n). Si vous voulez faire correspondre le caractère barre oblique inverse, vous aurez besoin de quatre barres obliques! 

La solution consiste à utiliser Go raw strings avec le backtick (') au lieu de guillemets doubles. Bien sûr, si vous souhaitez faire correspondre le caractère de nouvelle ligne, vous devez revenir aux chaînes habituelles et gérer plusieurs échappements de barre oblique inverse..

Placeholders et répétitions

Dans la plupart des cas, vous n'essayez pas de faire correspondre littéralement une séquence de caractères spécifiques telle que "abc", mais une séquence de longueur inconnue avec peut-être des caractères connus injectés quelque part. Les expressions régulières supportent ce cas d'utilisation avec le point  . caractère spécial qui représente n'importe quel caractère. le * caractère spécial répète le caractère précédent (ou groupe) zéro ou plusieurs fois. Si vous les combinez, comme dans .*, alors vous faites correspondre n'importe quoi car cela signifie simplement zéro ou plusieurs caractères. le + est très similaire à *, mais cela correspond à un ou plusieurs des personnages ou groupes précédents. Alors .+ correspondra à tout texte non vide.

Utilisation des limites

Il existe trois types de limites: le début du texte indiqué par ^, la fin du texte désigné par $, et la limite de mot indiquée par \ b. Par exemple, considérons ce texte du film classique La princesse à marier: "Je m'appelle Inigo Montoya. Vous avez tué mon père. Préparez-vous à mourir." Si vous ne faites correspondre que "père", vous obtenez une correspondance, mais si vous recherchez "père" à la fin du texte, vous devez ajouter le mot $ caractère, et puis il n'y aura pas de correspondance. Par contre, faire correspondre "Bonjour" au début fonctionne bien.

func main () text: = "Bonjour, je m'appelle Inigo Montoya, vous avez tué mon père, préparez-vous à mourir." motif: = correspondance "père" (motif, texte) motif = "père $" correspondance (motif, texte) motif = "^ Bonjour" correspondance (motif, texte) Sortie: √ père: Bonjour, je m'appelle Inigo Montoya, Tu as tué mon père, prépare toi à mourir. X père $: Bonjour, je m'appelle Inigo Montoya, vous avez tué mon père, préparez-vous à mourir. √ ^ Bonjour: Bonjour, je m'appelle Inigo Montoya, vous avez tué mon père, préparez-vous à mourir. 

Les limites de mots regardent chaque mot. Vous pouvez commencer et / ou terminer un motif avec le bouton \ b. Notez que les signes de ponctuation tels que les virgules sont considérés comme une limite et ne pas une partie du mot. Voici quelques exemples:

func main () text: = 'Bonjour, je m'appelle Inigo Montoya, vous avez tué mon père, préparez-vous à mourir.' pattern: = "kill" match (pattern, text) pattern = "\ bkill" match (pattern, text) pattern = "kill \ b" match (pattern, text) pattern = "\ bkill \ b" match (pattern, text ) pattern = '\ bkilled \ b' match (pattern, text) pattern = '\ bMontoya, \ b' match (pattern, text) Sortie: √ kill: Bonjour, mon nom est Inigo Montoya, vous avez tué mon père, préparez-vous mourir. √ \ bkill: Bonjour, je m'appelle Inigo Montoya, vous avez tué mon père, préparez-vous à mourir. X kill \ b: Bonjour, je m'appelle Inigo Montoya, vous avez tué mon père, préparez-vous à mourir. X \ bkill \ b: Bonjour, je m'appelle Inigo Montoya, vous avez tué mon père, préparez-vous à mourir. √ \ bkilled \ b: Bonjour, je m'appelle Inigo Montoya, vous avez tué mon père, préparez-vous à mourir. X \ bMontoya, \ b: Bonjour, je m'appelle Inigo Montoya, vous avez tué mon père, préparez-vous à mourir.

Utiliser des classes

Il est souvent utile de traiter tous les groupes de caractères comme des chiffres, des espaces ou des caractères alphanumériques. Golang supporte les classes POSIX, qui sont:

Classe de personnage Sens
[: alnum:]
alphanumérique ([0-9A-Za-z])
[:alpha:]
alphabétique (≡ [A-Za-z])
[: ascii:] 
ASCII (≡ [\ x00- \ x7F])
[:blanc:] 
vierge (≡ [\ t])
[: cntrl:]
contrôle (≡ [\ x00- \ x1F \ x7F])
[:chiffre:]
chiffres (≡ [0-9])
[:graphique:]
graphical (≡ [! - ~] == [A-Za-z0-9! "# $% & '() * +, \ -. / :;<=>?@ [\\\] ^ _ '| ~])
[:inférieur:] 
minuscule (≡ [a-z])
[:impression:] 
imprimable (≡ [- ~] == [[: graph:]])
[: punct:]
ponctuation (≡ [! - /: - @ [- '- ~])
[:espace:]
espace blanc (≡ [\ t \ n \ v \ f \ r])
[:plus haut:]
majuscule (≡ [A-Z])
[:mot:]
caractères de mot (≡ [0-9A-Za-z_])
[: xdigit:]
chiffre hexadécimal (≡ [0-9A-Fa-f])

Dans l'exemple suivant, je vais utiliser le [:chiffre:] classe pour rechercher des nombres dans le texte. Aussi, je montre ici comment rechercher un nombre exact de caractères en ajoutant le nombre demandé entre accolades.

func main () text: = 'La réponse à la vie, à l'univers et à tout est 42. "pattern: =" [[: digit:]] 3 "correspondance (pattern, texte) pattern =" [[: digit: ]] 2 "correspondance (motif, texte) Sortie: X [[: digit:]] 3: La réponse à la vie, à l'univers et à tout est 42. √ [[: digit:]] 2: La réponse à la vie, à l'univers et à tout est 42. 

Vous pouvez également définir vos propres classes en mettant les caractères entre crochets. Par exemple, si vous souhaitez vérifier si du texte est une séquence d'ADN valide ne contenant que les caractères ACGT puis utilisez le ^ [ACGT] * $ regex:

func main () text: = "AGGCGTTGGGAACGTT" modèle: = "^ [ACGT] * $" correspondance (modèle, texte) text = "Pas exactement une séquence d'ADN" correspondance (modèle, texte) Sortie: √ ^ [ACGT ] * $: AGGCGTTGGGAACGTT X ^ [ACGT] * $: Pas exactement une séquence d'ADN

Utiliser des alternatives

Dans certains cas, il existe plusieurs alternatives viables. Les URL HTTP correspondantes peuvent être caractérisées par un schéma de protocole, qui est soit http: // ou https: //. Le personnage de pipe | vous permet de choisir entre des alternatives. Voici une regex qui va les trier: (http) | (https): // \ w + \. \ w 2,. Il se traduit par une chaîne qui commence par http: // ou https: // suivi d'au moins un caractère de mot suivi d'un point suivi d'au moins deux caractères de mot.

func main () modèle: = '(http) | (https): // \ w + \. \ w 2,' correspondance (modèle, "http://tutsplus.com") correspondance (modèle, "https : //tutsplus.com ") correspond (motif," htt: //tutsplus.com ") Sortie: √ (http) | (https): // \ w + \. \ w 2,: http: / /tutsplus.com √ (http) | (https): // \ w + \. \ w 2,: https://tutsplus.com X (http) | (https): // \ w + \. \ w 2,: htt: //tutsplus.com

Conclusion

Dans cette partie du didacticiel, nous avons couvert beaucoup de terrain et appris beaucoup de choses sur les expressions régulières, avec des exemples pratiques utilisant la bibliothèque d'expressions rationnelles Golang. Nous nous sommes concentrés sur l'appariement pur et sur la façon d'exprimer nos intentions à l'aide d'expressions régulières. 

Dans la deuxième partie, nous nous concentrerons sur l'utilisation d'expressions régulières pour travailler avec du texte, notamment la recherche floue, les remplacements, le regroupement et la gestion de nouvelles lignes..