Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

GO e uso delle espressioni regolari

Come usare le espressioni regolari in GO e quale è la loro importanza nell'analisi del formato di un'informazione
Come usare le espressioni regolari in GO e quale è la loro importanza nell'analisi del formato di un'informazione
Link copiato negli appunti

Le espressioni regolari sono un potente strumento per la definizione di modelli (li chiameremo a volte pattern) di stringhe. Se ad esempio volessimo
verificare che una stringa fornita come targa automobilistica sia effettivamente composta da due lettere maiuscole, tre numeri e altre due lettere maiuscole dovremmo attivare una serie di funzioni sulle stringhe.

Ciò si complicherebbe se volessimo verificare il formato di tipi di informazioni più articolate quali numeri di telefono, indirizzi IP, e-mail o altro ancora.

Impiegando le espressioni regolari ci basterebbe invece dire che il formato da rispettare è [A-Z]{2}[0-9]{3}[A-Z]{2} e attivare le funzioni del linguaggio GO capaci di interpretarlo ed utilizzarlo. Facciamo un passo alla volta.

Definizione di un'espressione regolare

Per poter specificare il formato che ci interessa dobbiamo conoscere una serie di regole alla base delle espressioni regolari. Di seguito, indichiamo le più comuni:

  • .: il punto indica un carattere qualsiasi;
  • []: le parentesi quadre introducono un elenco di caratteri che possono essere usati in un certo punto della stringa o un intervallo di valori se si usa il trattino al loro interno. Ad esempio, [abc] indica l'uso di un carattere tra a, b e c mentre [A-Z] indica una lettera maiuscola compresa tra A a Z. Apponendo un simbolo ^ all'interno delle quadre si intende "tutto tranne ciò è indicato di seguito", pertanto [^abc] indica un carattere qualsiasi ad eccezione di a, b o c;
  • ?: l'elemento a cui è applicato è opzionale (appare una volta o per niente);
  • +: l'elemento a cui è applicato appare almeno una volta e può essere ripetuto quante vole si desidera;
  • *: l'elemento a cui è applicato può non apparire per niente, una sola volta o essere ripetuto quanto si vuole;
  • {}: le parentesi graffe possono indicare che un elemento deve apparire esattamente n volte ({n}), al massimo n volte ({,n}), minimo n volte ({n,});
  • ^ (al di fuori delle parentesi quadre): quanto segue costituisce l'inizio della stringa;
  • $: quanto appena indicato deve costituire la fine della stringa;
  • (elemento1|elemento2|elemento3): indica un elemento (anche articolato) a scelta tra elemento1, elemento2 ed elemento3;
  • esistono dei caratteri speciali, introdotti da un backslash, che indicano set di caratteri: \d indica una cifra numerica, \w un qualsiasi carattere, \s un qualsiasi spazio bianco ma ne esistono molti altri.

Queste sono solo le regole basilari ma il discorso potrebbe dilungarsi: il bello di tutto ciò è che il formato delle espressioni regolari viene usato in ogni linguaggio quindi una volta imparato lo si può considerare un know-how generale e altamente riutilizzabile.

Nonostante la carrellata di concetti sia stata velocissima, ora è chiaro perché [A-Z]{2}[0-9]{3}[A-Z]{2} riconoscerebbe "AB123CD" ma non "ab78FFF"? Perché esprime chiaramente che si richiedono:

  • due lettere maiuscole ([A-Z]{2});
  • tre numeri ([0-9]{3});
  • infine altre due maiuscole ([A-Z]{2}).

Le espressioni regolari possono essere però sfruttate in vari modi a seconda di quale funzione la stia trattando: per questo, dobbiamo conoscere il package regexp che GO mette a disposizione.

Funzioni per espressioni regolari

Di funzioni ve ne sarebbero molte da trattare ma qui vogliamo concentrarci in particolare su quelle legate alla verifica dell'espressione e alla ricerca di sottostringhe corrispondenti al pattern specificato. Vedremo:

  • MatchString: restituisce un vero booleano se rileva corrispondenza tra pattern e stringa analizzata, altrimenti un falso;
  • FindString: restituisce la prima occorrenza di una sottostringa rispondente al pattern;
  • FindStringIndex: restituisce posizione iniziale e posizione successiva a quella finale del punto in cui ha individuato la prima occorrenza di una sottostringa rispondente al pattern;
  • FindAllString: restituisce un elenco con tutte le occorrenze individuate. Si deve specificare un numero intero che dirà quante sottostringhe individuate vogliamo ottenere al massimo nel risultato. Se come intero specifichiamo -1 il risultato conterrà tutte le occorrenze individuate.

Prima di iniziare, è importante specificare un aspetto. Un'espressione regolare può essere utilizzata in due modi. Primo, come argomento di una delle funzioni del modulo regexp: in questo caso tale funzione verrà invocata direttamente come regexp.nome_funzione.

Altrimenti, esiste un approccio molto più efficiente, soprattutto se la stessa espressione deve essere richiamata più volte, che consiste nella compilazione dell'espressione mediante funzione Compile.

Quello che si otterrà come risultato da quest'ultima sarà un oggetto sul quale verrà richiamata la funzione da usare. Vedremo entrambi gli approcci.

Dopo aver importato il modulo regexp e gli altri necessari digitiamo:

import (
    "fmt"
    "regexp"
)
usiamo MatchString per vedere se due targhe (o presunte tali) sono valide:

verifica, _ := regexp.MatchString("[A-Z]{2}[0-9]{3}[A-Z]{2}", "AB123CD")
fmt.Println(verifica)
verifica, _ = regexp.MatchString("[A-Z]{2}[0-9]{3}[A-Z]{2}", "a4423GHG")
fmt.Println(verifica)

L'ouput sarà:

true
false

in quanto AB123CD appartiene al dominio del pattern [A-Z]{2}[0-9]{3}[A-Z]{2} ma a4423GHG no.

Qui l'espressione non è stata compilata infatti MatchString è stata invocata direttamente sul modulo regexp. Proviamo di nuovo compilandola:

// compilazione dell'espressione regolare
regex, _ := regexp.Compile("[A-Z]{2}[0-9]{3}[A-Z]{2}")
verifica = regex.MatchString("AB123CD")
fmt.Println(verifica)
verifica = regex.MatchString("a4423GHG")
fmt.Println(verifica
)

Come si vede la compilazione ha generato l'oggetto regex e su questo è stata invocata la funzione, ottenendo ovviamente i medesimi risultati. Proviamo a farci restituire ora
la stringa che è stata catturata grazie al pattern:

messaggio:="La targa della mia auto è AB123CD"
fmt.Println(regex.FindString(messaggio))

otteniamo:

AB123CD

ovvero la targa individuata.

Per ottenere le posizioni di inizio e fine di una sottostringa rispondente ad un pattern usiamo invece FindStringIndex:

posizione:=regex.FindStringIndex(messaggio)
fmt.Printf("Nel messaggio, la mia targa inizia in posizione %d ed è già finita in %d\n", posizione[0], posizione[1])

Grazie a FindStringIndex sapremo che:

Nel messaggio, la mia targa inizia in posizione 27 ed è già finita in 34

e così potremo usare queste coordinate per operare delle estrazioni dal testo.

Infine, chiediamo tutte le targhe aderenti al pattern che possono essere rinvenute in una stringa:

testo:="Le targhe in questione sono AB123GG, ab456XX, AC789XY"
fmt.Println(regex.FindAllString(testo, -1))

Ciò che recupereremo sarà la seguente lista:

[AB123GG AC789XY]

Non verrà invece recuperata la seconda targa che, seppur teoricamente valida, non rispetta tutti gli standard del pattern.

Si noti che in questi ultimi esempi abbiamo sempre fatto uso di un'espressione compilata. Per chiarezza, riportiamo un esempio eseguibile che racchiuda quanto illustrato:

package main
import (
    "fmt"
    "regexp"
)
func main() {
    verifica, _ := regexp.MatchString("[A-Z]{2}[0-9]{3}[A-Z]{2}", "AB123CD")
    fmt.Println(verifica)
    verifica, _ = regexp.MatchString("[A-Z]{2}[0-9]{3}[A-Z]{2}", "a4423GHG")
    fmt.Println(verifica)
    // compilazione dell'espressione regolare
    regex, _ := regexp.Compile("[A-Z]{2}[0-9]{3}[A-Z]{2}")
    verifica = regex.MatchString("AB123CD")
    fmt.Println(verifica)
    verifica = regex.MatchString("a4423GHG")
    fmt.Println(verifica)
    messaggio:="La targa della mia auto è AB123CD"
    fmt.Println(regex.FindString(messaggio))
    posizione:=regex.FindStringIndex(messaggio)
    fmt.Printf("Nel messaggio, la mia targa inizia in posizione %d ed è già finita in %d\n", posizione[0], posizione[1])
    testo:="Le targhe in questione sono AB123GG, ab456XX, AC789XY"
    fmt.Println(regex.FindAllString(testo, -1))
}

Quanto visto pone solo le basi dell'uso di espressioni regolari ma ne dimostra senz'altro la grande potenza nell'analisi del formato di un'informazione.


Ti consigliamo anche