Espressioni regolari con Go parte 2

Panoramica

Questa è la seconda parte di una serie di tutorial in due parti sulle espressioni regolari in Go. Nella prima parte abbiamo appreso quali sono le espressioni regolari, come esprimerle in Go e le basi dell'utilizzo della libreria regexp di Go per far corrispondere il testo ai pattern di espressioni regolari. 

Nella seconda parte, ci concentreremo sull'uso della libreria regexp in tutta la sua estensione, inclusa la compilazione di espressioni regolari, la ricerca di una o più corrispondenze nel testo, la sostituzione di espressioni regolari, il raggruppamento di submatch e la gestione di nuove righe.

Utilizzando la libreria Regexp

La libreria regexp offre un supporto completo per le espressioni regolari e la possibilità di compilare i tuoi pattern per un'esecuzione più efficiente quando si utilizza lo stesso pattern per confrontarsi con più testi. Puoi anche trovare gli indici delle partite, sostituire le partite e usare i gruppi. Tuffiamoci dentro.

Compilando il tuo Regex

Esistono due metodi per compilare regex: Compilare() e MustCompile (). Compilare() restituirà un errore se il modello fornito non è valido. MustCompile () andrà nel panico. La compilazione è consigliata se ti interessano le prestazioni e prevedi di utilizzare più volte la stessa espressione regolare. Cambiamo il nostro incontro() funzione di supporto per eseguire una regex compilata. Si noti che non è necessario verificare la presenza di errori perché la regex compilata deve essere valida.

func match (r * regexp.Regexp, stringa di testo) matched: = r.MatchString (testo) se trovato fmt.Println ("√", r.String (), ":", text) else fmt. Println ("X", r.String (), ":", testo) 

Ecco come compilare e utilizzare la stessa regex compilata più volte:

func main () es: = '(\ bcats? \ b) | (\ bdogs? \ b) | (\ brats? \ b)' e: = regexp.MustCompile (es) corrisponde (e, "Sta piovendo i cani e gatti ") corrisponde (e," Il catalogo è pronto. È tempo di hot dog! ") corrisponde (e," È un cane mangia il mondo del cane. ") Output: √ (\ bcats? \ b) | (\ bdogs? \ b) | (\ brats? \ b): piove a cani e gatti X (\ bcats? \ b) | (\ bdogs? \ b) | (\ brats? \ b): Il catalogo è pronto. È tempo di hot dog! √ (\ bcats? \ B) | (\ bdogs? \ B) | (\ brats? \ B): È un cane che mangia il mondo del cane. 

scoperta

L'oggetto Regexp ha a lotto di FindXXX () metodi. Alcuni di essi restituiscono la prima corrispondenza, altri restituiscono tutte le corrispondenze e altri ancora restituiscono un indice o indici. È interessante notare che i nomi di tutti i 16 metodi di funzioni corrispondono alla seguente espressione regolare: Trova (Tutti)? (String)? (Submatch)? (Index)?

Se è presente "Tutto", tutte le corrispondenze vengono restituite rispetto a quella più a sinistra. Se è presente "String", il testo di destinazione ei valori di ritorno sono stringhe e matrici di byte. Se è presente "Submatch", vengono inviati i submatch (gruppi) rispetto alle corrispondenze semplici. Se è presente "Indice", gli indici all'interno del testo di destinazione vengono restituiti rispetto alle corrispondenze effettive.

Prendiamo una delle funzioni più complesse per il compito e utilizzare il FindAllStringSubmatch () metodo. Prende una stringa e un numero n. Se n è -1, restituirà tutti gli indici corrispondenti. Se n è un numero intero non negativo, restituirà le n corrispondenze più a sinistra. Il risultato è una porzione di sezioni di stringa. 

Il risultato di ogni submatch è la corrispondenza completa seguita dal gruppo catturato. Ad esempio, prendi in considerazione un elenco di nomi in cui alcuni di essi hanno titoli come "Mr.", "Mrs." o "Dr.". Ecco una regex che cattura il titolo come submatch e quindi il resto del nome dopo uno spazio: \ b (Mr \. | Mrs \. | Dr \.). *.

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

Come puoi vedere nell'output, la partita intera viene catturata per prima e solo il titolo. Per ogni riga, la ricerca si ripristina.

Sostituzione

Trovare le partite è fantastico, ma spesso potrebbe essere necessario sostituire la partita con qualcos'altro. L'oggetto Regexp ha diversi ReplaceXXX () metodi come al solito per gestire stringhe contro array di byte e sostituzioni letterarie contro espansioni. Nel grande libro 1984 di George Orwell, gli slogan del partito sono incisi sulla piramide bianca del ministero della verità: 

  • La guerra è pace 
  • La libertà è schiavitù 
  • L'ignoranza è forte 

Ho trovato un piccolo saggio su The Price of Freedom che utilizza alcuni di questi termini. Correggiamo uno snippet di esso secondo il doppione del party usando Go regexes. Si noti che alcune parole di destinazione per la sostituzione utilizzano diverse lettere maiuscole. La soluzione è aggiungere il flag senza distinzione tra maiuscole e minuscole (io?) all'inizio della regex. 

Dal momento che la traduzione è diversa a seconda dei casi, abbiamo bisogno di un approccio più sofisticato rispetto alla sostituzione letterale. Fortunatamente (o di progettazione), l'oggetto Regexp ha un metodo di sostituzione che accetta una funzione che utilizza per eseguire la sostituzione effettiva. Definiamo la nostra funzione replacer che restituisce la traduzione con il caso corretto.

func replacer (s string) string d: = map [stringa] stringa "war": "peace", "WAR": "PEACE", "War": "Peace", "freedom": "slavery", " LIBERTA ': "SCHIAVITÙ", "Libertà": "Schiavitù", "ignoranza": "forza", "IGNORANZA": "FORZA", "Ignoranza": "Forza", r, ok: = d [s] se ok return r else return s 

Ora possiamo eseguire la sostituzione effettiva:

func main () text: = 'IL PREZZO DI LIBERTA': Americans at War americani sono andati in guerra per conquistare la loro indipendenza, espandere i loro confini nazionali, definire le loro libertà e difendere i loro interessi in tutto il mondo. ' expr: = '(? i) (war | freedom | ignorance)' r: = regexp.MustCompile (expr) result: = r.ReplaceAllStringFunc (text, replacer) fmt.Println (risultato) Output: IL PREZZO DELLA SCHIAVA: Gli americani per la pace Gli americani sono andati in pace per conquistare la loro indipendenza, espandere i loro confini nazionali, definire i loro schiavi e difendere i loro interessi in tutto il mondo. 

L'output è alquanto incoerente, che è il segno distintivo di una buona propaganda.

Raggruppamento

Abbiamo visto come usare il raggruppamento con i submatch in precedenza. Ma a volte è difficile gestire più submatch. I gruppi nominati possono aiutare molto qui. Ecco come denominare i gruppi di submatch e popolare un dizionario per un facile accesso per nome:

func main () e: = '(? P\ w +) (? P.+ )? (? P\ w +) 'r: = nomi regexp.MustCompile (e): = r.SubexpNames () fullNames: = [] string ' John F. Kennedy ',' Michael Jordan ' per _, fullName: = range fullNames result : = r.FindAllStringSubmatch (fullName, -1) m: = map [stringa] stringa  per i, n: = intervallo risultato [0] m [nomi [i]] = n fmt.Println ("nome : ", m [" first "]) fmt.Println (" middle_name: ", m [" middle "]) fmt.Println (" last name: ", m [" last "]) fmt.Println () Output: nome: John middle_name: F. cognome: Kennedy nome: Michael middle_name: cognome: Jordan

Trattare con nuove linee

Se ricordi, ho detto che il carattere speciale punto corrisponde a qualsiasi carattere. Bene, ho mentito. Non corrisponde alla nuova riga (\ n) carattere per impostazione predefinita. Ciò significa che le tue partite non attraverseranno le linee a meno che non lo specifichi esplicitamente con la bandiera speciale (?S) che puoi aggiungere all'inizio della tua regex. Ecco un esempio con e senza la bandiera.

func main () text: = "1111 \ n2222" expr: = [] stringa ". *", "(? s). *" per _, e: = intervallo expr r: = regexp.MustCompile ( e) risultato: = r.FindString (testo) result = strings.Replace (risultato, "\ n", '\ n', -1) fmt.Println (e, ":", risultato) fmt.Println ()  Output:. *: 1111 (? S). *: 1111 \ n2222 

Un'altra considerazione è se trattare il ^ e $ caratteri speciali come inizio e fine dell'intero testo (predefinito) o come inizio e fine di ogni riga con il (? M) bandiera.  

Conclusione

Le espressioni regolari sono uno strumento potente quando si lavora con testo semi-strutturato. Puoi usarli per convalidare l'input testuale, pulirlo, trasformarlo, normalizzarlo, e in generale gestire molta diversità usando la sintassi concisa. 

Go fornisce una libreria con un'interfaccia facile da usare che consiste in un oggetto Regexp con molti metodi. Fare un tentativo, ma attenzione alle insidie.