Espressioni regolari con Go parte 1

Panoramica

Le espressioni regolari (regex di AKA) sono un linguaggio formale che definisce una sequenza di caratteri con qualche schema. Nel mondo reale possono essere usati per risolvere molti problemi con il testo semi-strutturato. Puoi estrarre i pezzi importanti dal testo con molte decorazioni o contenuti non correlati. Go ha un pacchetto regex forte nella sua libreria standard che ti permette di tagliare e tagliare il testo con espressioni regolari. 

In questa serie in due parti, imparerai quali sono le espressioni regolari e come utilizzare efficacemente le espressioni regolari in Vai per eseguire molte attività comuni. Se non hai familiarità con le espressioni regolari, ci sono molti tutorial fantastici. Eccone uno buono.

Comprendere le espressioni regolari

Iniziamo con un rapido esempio. Hai del testo e vuoi controllare se contiene un indirizzo email. Un indirizzo email è specificato rigorosamente in RFC 822. In breve, ha una parte locale seguita da un simbolo @ seguito da un dominio. L'indirizzo di posta sarà separato dal resto del testo dallo spazio. 

Per capire se contiene un indirizzo email, la seguente regex farà: ^ \ W + @ \ w + \. \ W + $. Tieni presente che questa espressione regolare è un po 'permissiva e consentirà alcuni indirizzi email non validi. Ma è abbastanza buono per dimostrare il concetto. Proviamo su un paio di potenziali indirizzi email prima di spiegare come funziona:

pacchetto main import ("os" "regexp" "fmt") func check (errore err) if err! = nil fmt.Println (err.Error ()) os.Exit (1) func main ()  email: = [] string "brown @ fox", "brown @ fox.", "[email protected]", "br @ own @ fox.com", pattern: = '^ \ w + @ \ w + \ . \ w + $ 'per _, email: = indirizzi email matched, err: = regexp.Match (pattern, [] byte (email)) controlla (err) se trovato fmt.Printf ("√'% s 'is una email valida \ n ", email) else fmt.Printf (" X '% s' non è un'e-mail valida \ n ", email) Output: X 'brown @ fox' non è un'e-mail valida X '@ brown fox.' non è un'e-mail valida √ '[email protected]' è un'e-mail valida X 'br @ own @ fox.com' non è un'e-mail valida

La nostra espressione regolare funziona su questo piccolo campione. I primi due indirizzi sono stati rifiutati perché il dominio non aveva un punto o non aveva caratteri dopo il punto. La terza email è stata formattata correttamente. L'ultimo candidato aveva due simboli @.

Rompiamo questa regex: ^ \ W + @ \ w + \. \ W + $

Personaggio / Simbolo Senso
^ Inizio del testo di destinazione
\ w Caratteri di qualsiasi parola [0-9A-Za-z_]
+ Almeno uno dei personaggi precedenti
@ Letteralmente il personaggio @ 
\. Il carattere letterale del punto. Deve essere sfuggito con \
$ Fine del testo di destinazione

Complessivamente, questa regex corrisponderà a parti di testo che iniziano con uno o più caratteri di parola, seguiti dal carattere "@", seguiti di nuovo da uno o più caratteri di parola, seguiti da un punto e seguiti da ancora uno o più caratteri di parola.  

Trattare con caratteri speciali

I seguenti personaggi hanno significati speciali nelle espressioni regolari: .+? * () | [] ^ $ \. Ne abbiamo già visti molti nell'esempio di email. Se vogliamo abbinarli letteralmente, dobbiamo sfuggire a loro con una barra rovesciata. Introduciamo una piccola funzione di aiuto chiamata incontro() questo ci salverà un sacco di battitura. Prende un modello e del testo, usa il regexp.Match () metodo per far corrispondere il modello al testo (dopo la conversione del testo in un array di byte) e stampa i risultati:

func match (stringa di pattern, stringa di testo) matched, _: = regexp.Match (pattern, [] byte (testo)) se trovato fmt.Println ("√", pattern, ":", text) else  fmt.Println ("X", pattern, ":", testo)

Ecco un esempio di corrispondenza con un personaggio normale come z contro la corrispondenza con un personaggio speciale come ?:

func main () text: = "Posso haz cheezburger?" pattern: = "z" match (pattern, text) pattern = "\\?" match (pattern, text) pattern = '\?' match (pattern, text) Output: √ z: Posso haz cheezburger? √ \? : Posso haz cheezburger? √ \? : Posso haz cheezburger? 

Il modello regex \? contiene una barra rovesciata che deve essere sottoposta a escape con un'altra barra rovesciata se rappresentata come una normale stringa Go. Il motivo è che il backslash viene anche usato per sfuggire ai caratteri speciali nelle stringhe Go come newline (\ n). Se vuoi abbinare il carattere backslash, avrai bisogno di quattro barre! 

La soluzione è usare le stringhe raw Go con il backtick (') invece di virgolette. Naturalmente, se vuoi abbinare il carattere di nuova riga, devi tornare alle stringhe regolari e gestire più escape di backslash.

Segnaposti e ripetizioni

Nella maggior parte dei casi, non si tenta di far corrispondere letteralmente una sequenza di caratteri specifici come "abc", ma una sequenza di lunghezza sconosciuta con forse alcuni caratteri noti iniettati da qualche parte. Regex supporta questo caso d'uso con il punto  . personaggio speciale che rappresenta qualsiasi personaggio. Il * il carattere speciale ripete il carattere precedente (o il gruppo) zero o più volte. Se li unisci, come in .*, allora abbini qualcosa perché significa semplicemente zero o più caratteri. Il + è molto simile a *, ma corrisponde a uno o più dei precedenti caratteri o gruppi. Così .+ corrisponderà a qualsiasi testo non vuoto.

Usare i confini

Esistono tre tipi di limiti: l'inizio del testo indicato da ^, la fine del testo indicato da $, e il limite della parola indicato da \ b. Ad esempio, considera questo testo dal film classico La sposa principessa: "Mi chiamo Inigo Montoya, hai ucciso mio padre, prepari a morire". Se si abbina solo a "padre" si ottiene una corrispondenza, ma se si sta cercando "padre" alla fine del testo, è necessario aggiungere il $ personaggio, e quindi non ci sarà alcuna corrispondenza. D'altra parte, l'abbinamento "Ciao" all'inizio funziona bene.

func main () text: = "Ciao, mi chiamo Inigo Montoya, hai ucciso mio padre, ti prepari a morire." pattern: = "padre" match (pattern, text) pattern = "father $" match (pattern, text) pattern = "^ Hello" match (pattern, text) Output: √ padre: Ciao, mi chiamo Inigo Montoya, hai ucciso mio padre, preparati a morire. X padre $: Ciao, mi chiamo Inigo Montoya, hai ucciso mio padre, ti sei preparato a morire. √ ^ Ciao: Ciao, mi chiamo Inigo Montoya, hai ucciso mio padre, ti sei preparato a morire. 

I confini della parola guardano ogni parola. Puoi iniziare e / o terminare un pattern con il \ b. Si noti che i segni di punteggiatura come le virgole sono considerati un limite e non parte della parola. Ecco alcuni esempi:

func main () text: = 'Ciao, mi chiamo Inigo Montoya, hai ucciso mio padre, ti prepari a morire.' pattern: = 'kill' match (pattern, text) pattern = '\ bkill' match (pattern, text) pattern = 'kill \ b' match (pattern, text) pattern = '\ bkill \ b' match (pattern, testo ) pattern = '\ bkilled \ b' match (pattern, text) pattern = '\ bMontoya, \ b' match (pattern, text) Output: √ kill: Ciao, mi chiamo Inigo Montoya, hai ucciso mio padre, prepari morire. √ \ bkill: Ciao, mi chiamo Inigo Montoya, hai ucciso mio padre, ti prepari a morire. X kill \ b: Ciao, mi chiamo Inigo Montoya, hai ucciso mio padre, ti prepari a morire. X \ bkill \ b: Ciao, mi chiamo Inigo Montoya, hai ucciso mio padre, ti sei preparato a morire. √ \ bkilled \ b: Ciao, mi chiamo Inigo Montoya, hai ucciso mio padre, ti sei preparato a morire. X \ bMontoya, \ b: Ciao, mi chiamo Inigo Montoya, hai ucciso mio padre, ti sei preparato a morire.

Uso delle classi

È spesso utile trattare tutti i gruppi di caratteri insieme come tutte le cifre, i caratteri dello spazio bianco o tutti i caratteri alfanumerici. Golang supporta le classi POSIX, che sono:

Classe di caratteri Senso
[: Alnum:]
alfanumerico (≡ [0-9A-Za-z])
[:alfa:]
alfabetico (≡ [A-Za-z])
[: Ascii:] 
ASCII (≡ [\ x00- \ x7F])
[: Blank:] 
vuoto (≡ [\ t])
[: Cntrl:]
controllo (≡ [\ x00- \ x1F \ x7F])
[: Digit:]
cifre (≡ [0-9])
[:grafico:]
grafico (≡ [! - ~] == [A-Za-z0-9! "# $% & '() * +, \ -. / :;<=>?@ [\\\] ^ _ '| ~])
[:inferiore:] 
minuscolo (≡ [a-z])
[:stampare:] 
stampabile (≡ [- ~] == [[: grafico:]])
[: Punct:]
punteggiatura (≡ [! - /: - @ [- '- ~])
[:spazio:]
spazio bianco (≡ [\ t \ n \ v \ f \ r])
[:superiore:]
maiuscolo (≡ [A-Z])
[:parola:]
caratteri di parole (≡ [0-9A-Za-z_])
[: Xdigit:]
cifra esadecimale (≡ [0-9A-Fa-f])

Nell'esempio seguente, userò il [: Digit:] classe per cercare numeri nel testo. Inoltre, mostro qui come cercare un numero esatto di caratteri aggiungendo il numero richiesto tra parentesi graffe.

func main () text: = 'La risposta alla vita, universo e tutto è 42. "pattern: =" [[: digit:]] 3 "match (pattern, text) pattern =" [[: digit: ]] 2 "match (pattern, text) Output: X [[: digit:]] 3: La risposta alla vita, universo e tutto è 42. √ [[: digit:]] 2: La risposta alla vita, all'universo e tutto è 42. 

Puoi anche definire le tue classi inserendo i caratteri tra parentesi quadre. Ad esempio, se si desidera verificare se un testo è una sequenza di DNA valida che contiene solo i caratteri ACGT quindi utilizzare il ^ [ACGT] * $ regex:

func main () text: = "AGGCGTTGGGAACGTT" pattern: = "^ [ACGT] * $" match (pattern, text) text = "Non esattamente una sequenza di DNA" match (pattern, text) Output: √ ^ [ACGT ] * $: AGGCGTTGGGAACGTT X ^ [ACGT] * $: non esattamente una sequenza di DNA

Utilizzo di alternative

In alcuni casi, esistono più alternative valide. Gli URL HTTP corrispondenti possono essere caratterizzati da uno schema di protocollo, che è entrambi http: // o https: //. Il personaggio della pipa | ti consente di scegliere tra le alternative. Ecco una regex che li risolverà: (Http) | (https). // \ w + \ \ w 2,. Si traduce in una stringa che inizia con http: // o https: // seguito da almeno un carattere di parola seguito da un punto seguito da almeno due caratteri di parole.

func main () pattern: = '(http) | (https): // \ w + \. \ w 2,' match (pattern, "http://tutsplus.com") match (pattern, "https : //tutsplus.com ") match (pattern," htt: //tutsplus.com ") Output: √ (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

Conclusione

In questa parte del tutorial, abbiamo coperto molto terreno e imparato molto sulle espressioni regolari, con esempi pratici che utilizzano la libreria regexp di Golang. Ci siamo concentrati sulla pura corrispondenza e su come esprimere le nostre intenzioni usando espressioni regolari. 

Nella seconda parte, ci concentreremo sull'utilizzo di espressioni regolari per lavorare con il testo, tra cui la ricerca fuzzy, le sostituzioni, il raggruppamento e la gestione di nuove linee.