Introduzione alle Regular Expression – Seconda Parte

Regular Expresion - RegExQuesta è la seconda parte della serie Introduzione alle Regular Expression. Se non hai ancora letto la prima parte ti consiglio di farlo. Puoi usare la pagina demo per provare le query della guida.

Negazione [^]

Abbiamo già conosciuto la [cornice]. Una caratteristica importante di cui non abbiamo ancora parlato è la negazione. Supponiamo di voler cercare qualunque carattere eccetto la a.

[^a] # trova b,c,d,e,f,\n .... qualunque carattere eccetto a

La negazione si applica a tutti i caratteri della cornice in cui compare l’operatore ˆ. Non è possibile limitarla a solo alcuni.

[^0123456789] # trova qualunque carattere non numerico

Gli Alias

Ora siamo pronti per affrontare qualche esempio realistico di regular expression. Uno degli usi più frequenti delle regex è la convalida di Stringhe. Proviamo a verificare la correttezza di un ipotetico numero telefonico da esprimere nel formato 555-12345678. Di norma divideremmo l’input in due parti e proveremmo a convertirle in numeri. Ora tuttavia conosciamo le regex e possiamo sbrigarcela meglio.

555-[0-9]{7}

Fatto. Stiamo convalidando 555 seguito da un trattino seguito da 7 caratteri numerici. Possiamo essere addirittura più sintetici, vediamo come.

I range [0-9] e [a-z]  sono talmente frequenti da saltar fuori continuamente, tanto che sono state create scorciatoie (alias) dedicate ai range più usati. Nel nostro caso ci torna utile \d, che sta per digit (carattere numerico) ed è semanticamente identico a [0-9]

555-\d{7} # identico a 555-[0-9]{7}

Gli alias non sono certo indispensabili, puoi ottenere gli stessi risultati usando la cornice in modo esteso. Tuttavia sono molto comodi.

alias significato coorrisponde a
\d digit (numero) [0-9]
\w word (parola) [a-zA-Z0-9_]  Include il carattere underscore
\s spazio, tab o newline [ \t\r\n]
\D qualsiasi non numerico ^\d
\W quasiasi non alfanumerico ^\w
\S quasiasi ma non lo spazio ^\s

Suggerimento: nota come ad ogni alias ne corrisponde uno dal significato opposto, tutto in maiuscolo. Impara i primi tre per conoscerli tutti e sei.

Il Punto

Il punto è un alias un pò speciale. Ne parlo soprattutto perchè può capitare di notarlo nel codice scritto da altri. Il punto cerca tutto eccetto il new line (\n). Il problema è che il carattere new line non è lo stesso su tutte le piattaforme.

.   # trova tutti i caratteri
.*  # equivalente a [^\n], trova tutti i paragrafi

Il punto spesso crea confusione. Consiglio di ricorrere a combinazioni di alias e cornici per ottenere gli stessi risultati senza rischiare errori.

Escaping

Capita a volte di dover cercare proprio il punto, oppure i caratteri [ o ]. Poiché fanno parte della sintassi regex in questi casi è necessario riccorrere all’escaping, ovvero precederli col carattere \ che rappresenta l’escape. Per esempio

\.    # cerca il punto invece che tutto quanto
\*    # trova tutti gli asterischi
\\    # trova tutti gli escape

Raggruppamenti e OR

Torniamo alla convalida. Questa volta vogliamo verificare la validità di un indirizzo email. Prima di tutto stabiliamo le regole (semplificate) a cui una stringa deve attenersi per venire convalidata come indirizzo email:

  • il nome utente può contenere lettere, numeri, underscore e trattini ma deve cominciare con una lettera
  • il dominio può contenere solo lettere seguite da un punto seguito da altre lettere. Quindi domain.fakecom è valido per noi
[a-z][\w-]*@[a-z]+\.[a-z]+

Presta attenzione all’escaping del punto! Un’altro dettaglio importante è il quantificatore + che abbiamo visto nel primo articolo di questa serie. Nel dominio infatti vogliamo almeno una lettera, non zero o più.

Supponiamo ora di voler aggiornare le regole in modo da convalidare solo i domini più importanti. Il nostro indirizzo email deve finire con com oppure net. Non possiamo risolvere questo problema con quel che abbiamo imparato finora. La nostra query regex dovrà ricorrere a due nuovi concetti, l’operatore OR e i gruppi.

[a-z][\w-]*@[a-z]+\.(com|net)

Vediamo l’OR all’opera

com|net  #  trova com oppure net
a|b|c    # lo stesso di [abc].

L’aggiunta delle parentesi () si rende necessaria per chiarire che non vogliamo trovare tutte le occorrenze della parte di regex alla loro sinistra. Se volessimo ‘Brad Pitt’ oppure ‘Angelina Pitt’

Brad|Angelina Pitt  # trova sia 'Brad' che 'Angelina Pitt'
(Brad|Angelina) Pitt  # ora ci siamo!

Per un programmatore il concetto del raggruppamento (grouping) con le parentesi dovrebbe essere facilmente comprensibile. Di fatto possiamo combinarlo con altri operatori che già conosciamo

(dog)+   #  trova dog,dogdog,dogdogdog ...
java(bean)?    #  trova java o javabean

Conclusione

Questo conclude la seconda parte della guida. La prossima e ultima parte verrà pubblicata tra qualche giorno. Nel frattempo raccomando di giocare con la pagina demo per fare un po’ di pratica.

E’ ora disponibile anche la terza parte di questo tutorial.

Questo articolo è una traduzione autorizzata di Regex Primer: Part 2. Ringrazio l’autore per il permesso accordatomi.

  • Pingback: diggita.it

  • Pingback: Introduzione alle Regular Expression - Terza Parte

  • Alessandra

    ciao,
    non trovo la pagina demo :-(
    in alternativa che cosa si può usare per giocare con le espressioni regolari?
    Per cortesia su Linux :-)
    grazie
    ciao

    • http://nicolaiarocci.com/about/ Nicola Iarocci

      Alessandra, grazie per la segnalazione, ho aggiornato il link e ora è tutto a posto.

  • Ivan Ghidoni

    Ciao Nicola,
    grazie per la tua “Introduzione alle RegExp”,
    mi è stata molto utile!
    Ora però non trovo soluzione ad un mio problema..
    dal testo in cui applico la mia espressione,vorrei ottenere solo la prima sezione footprint (corrispondente alle righe 2,3,4,5,6) , mentre in realtà ottengo la prima e la seconda sezione :((

    Espressione:
    \(footprint Foot10000A[\w\W]*(?=\(footprint)

    Testo:
    (
    (footprint Foot10000A
    (AAAAAAAAAAAA)
    ; (BBBBBBBBBB)
    ; (utime 2006-06-23-10:18:29)
    )
    (footprint Foot10000B
    (DDDDDDDDDD)
    ; (CCCCCCCC)
    ; (ctime 2004-04-28-10:48:55)
    )
    (footprint Foot10000B
    (EEEEEEEEEE)
    ; (FFFFFFFF)
    ; (ctime 2007-07-27-08:33:26)
    )

    • http://nicolaiarocci.com/about/ Nicola Iarocci

      Ciao Ivan, scusa se ti rispondo con un po’ di ritardo. Prova con questa regex:
      \(footprint Foot10000A[\w\W]*?\)[\r\n]\)

      L’ho abbozzata rapidamente e probabilmente si può migliorare.

      • Ivan Ghidoni

        Ciao Nicola,
        ho trovato la soluzione:
        \(footprint Foot10000A[\w\W]*?(?=\(footprint)

        infatti bastava un ? dopo l’asterisco.
        Grazie

  • Enrico

    Ciao Nicola… ho un piccolo problema!!

    io ho una TextBox che è il Nome Utente.

    Questa deve contenere solo lettere (maiuscole o minuscole), numeri, underscore, trattino, e lettere accentate. Da un minimo di 5 caratteri fino alla lunghezza max della Text, ovvero impostata a 20.

    Ho messo la regex così:
    [\w-àèéìòù]

    ma non mi funziona… mi sapresti aiutare???

    Grazie mille

    • Gianni CARROZZO

      [\w-àèéìòù]{5,20}

      se i submatches sono più di uno il nome utente è incorretto.

      esempi:
      Cipp5_lp*-àèéìòùabcd — no buono
      Cipp — no buono
      Cipp5_lp-àèéìòùa — buono
      Cipp5_lp-àèéìòùab — buono
      Cipp5_lp-àèéìòùabc — buono
      Cipp5_lp-àèéìòùabcd — buono
      Cipp5_lp-àèéìòùabcde — buono
      Cipp5_lp-àèéìòùabcd* — buono, ma
      la differente lunghezza fra il submatch
      e il testo digitato deve produrre un errore

  • Rocco

    Io sto usando il tool “The Regex Coach” fa’ il suo mestiere

  • Gianni CARROZZO

    \(footprint Foot10000A[\r\n][\(\w\W\)]*?[\n\r]\)
    trova la prima sezione

    \(footprint Foot10000B[\r\n][\(\w\W\)]*?[\n\r]\)
    trova le altre due