espressioni regolari

metodo per verificare se una stringa ha una certa forma


cercare una stringa in un'altra

esempio: elenco telefonico

cerchiamo "Mario":

import re

elenco='Luca Rossi 2345234 - Mario Bianchi 82342342 - Marco Verdi 342342342'

m = re.search('Mario', elenco)
if m:
  print("trovato")
else:
  print("not trovato")

trovato.py


funzione di ricerca

import re
richiede l'uso delle espressioni regolari
re.search('Mario', elenco)
cerca la stringa 'Mario' nella stringa elenco

e il telefono?

if m:
  print("trovato")
else:
  print("not trovato")

dice solo se il nome c'è o meno

non dà il numero di telefono


stampa della stringa trovata

import re

elenco='Luca Rossi 2345234 - Mario Bianchi 82342342 - Marco Verdi 342342342'

m = re.search('Mario', elenco)
if m:
  print("trovato: " + m.group())
else:
  print("not trovato")

nome.py

m.group() è la stringa trovata

trovato: Mario

non c'è il telefono!


cosa cerchiamo davvero?

import re

elenco='Luca Rossi 2345234 - Mario Bianchi 82342342 - Marco Verdi 342342342'

m = re.search('Mario', elenco)
if m:
  print("trovato: " + m.group())
else:
  print("not trovato")

m = re.search('Mario', elenco) = cerca la stringa 'Mario' in elenco

serve nome+telefono!


ricerca nome + telefono

elenco='Luca Rossi 2345234 - Mario Bianchi 82342342 - Marco Verdi 342342342'

m = re.search('Mario Bianchi 82342342', elenco)
…

vorrebbe dire che già sappiamo il telefono

quello che cerchiamo:

Mario … -

cos'è '…' qui?


classi di caratteri e ripetizioni

formato dell'elenco:

elenco='Luca Rossi 2345234 - Mario Bianchi 82342342 - Marco Verdi 342342342'

cerchiamo:

Mario seguito da caratteri, spazi e cifre

basta che non ci sia -


classi di caratteri e ripetizioni

m = re.search('Mario[a-zA-Z0-9 ]*', elenco)
[a-zA-Z0-9 ]
un carattere qualsiasi fra lettere minuscole, maiuscole, cifre e lo spazio
*
ripetizione

quindi:

[a-zA-Z0-9 ]* è una sequenza di lettere, cifre e spazi

programma completo

import re

elenco='Luca Rossi 2345234 - Mario Bianchi 82342342 - Marco Verdi 342342342'

m = re.search('Mario[a-zA-Z0-9 ]*', elenco)
if m:
  print("trovato: " + m.group())
else:
  print("not trovato")

tutto.py

stampa:

trovato: Mario Bianchi 82342342

classi definite per negazione

elenco='Luca Rossi 2345234 - Mario Bianchi 82342342 - Marco Verdi 342342342'

in questo caso:

[a-zA-Z0-9 ] = caratteri qualsiasi tranne -

espressione: [^-]


ricerca in elenco, con negazione

import re

elenco='Luca Rossi 2345234 - Mario Bianchi 82342342 - Marco Verdi 342342342'

m = re.search('Mario[a-zA-Z0-9 ]*', elenco)
if m:
  print("trovato: " + m.group())
else:
  print("not trovato")

negazione.py

trovato: Mario Bianchi 82342342

separare i campi

import re

elenco='Luca Rossi 2345234 - Mario Bianchi 82342342 - Marco Verdi 342342342'

m = re.search('(Mario) ([A-Za-z]*) ([0-9]*)', elenco)
if m:
  print("trovato")
  print("nome: " + m.group(1))
  print("cognome: " + m.group(2))
  print("telefono: " + m.group(3))
else:
  print("not trovato")

campi.py


parti di un'espressione regolare

m = re.search('(Mario) ([A-Za-z]*) ([0-9]*)', elenco)
                -----   ---------   ------
            m.group(1)  m.group(2)  m.group(3)

m.group() è tutto
uguale a m.group(0)


stringa trovata

Luca Rossi 2345234 - Mario Bianchi 82342342 - Marco Verdi 342342342
                     ----- ------- --------
		      |       |        |
                 (Mario) ([A-Za-z]*) ([0-9]*)

la ricerca ha successo:

Mario
c'è
[A-Za-z]*
sequenza di lettere maiuscole e minuscole
Bianchi lo è
[0-9]*
sequenza di cifre
82342342 lo è

spazi

Luca Rossi 2345234 - Mario Bianchi 82342342 - Marco Verdi 342342342
                     ----- ------- --------
		      ↑   ↑   ↑   ↑    ↑
		      | +-+   |   +-+  |
		      | |     |     |  |
		 -------|-----------|--------
                 (Mario) ([A-Za-z]*) ([0-9]*)

gli spazi sono caratteri come gli altri

spazio nell'espressione regolare → spazio nella stringa


nomi che iniziano con M

re.search() trova solo il primo

re.finditer() li trova tutti


nomi che iniziano con M

import re

elenco='Luca Rossi 2345234 - Mario Bianchi 82342342 - Marco Verdi 342342342'

i = re.finditer('(M[a-z]*) ([A-Za-z]*) ([0-9]*)', elenco)
for m in i:
  print('nome: ' + m.group(1) + ' ' \
        'cognome: ' + m.group(2) + ' ' \
        'telefono: ' + m.group(3))

altri.py

nome: Mario cognome: Bianchi telefono: 82342342
nome: Marco cognome: Verdi telefono: 342342342

riassunto


seconda lettera del cognome

cercare tutte le persone che hanno il primo nome che inizia con M
e il cognome che inizia con Bi oppure Bo


seconda lettera del cognome

import re

elenco='Luca Rossi 2345234 - ' \
       'Mario Bianchi 82342342 - ' \
       'Marco Verdi 342342342 - ' \
       'Roberto Biscardi 3243456 - ' \
       'Bianca Bugatti 343425 - ' \
       'Massimo Bologna 343245 - ' \
       'Giorgio Mona 3423424 - '

i = re.finditer('(M[a-z]*) (B[io][a-z]*) ([0-9]*)', elenco)
for m in i:
  print(m.group())
  print('    nome: ' + m.group(1))
  print('    cognome: ' + m.group(2))
  print('    telefono: ' + m.group(3))

seconda.py

il cognome viene trovato da B[io][a-z]*:


parentesi quadre

sia intervalli che caratteri elencati uno per uno


ripetizione

sia intervalli che singoli caratteri


ultima lettera del nome

trovare solo le persone il cui nome termina con o


ultima lettera del nome

import re

…

i = re.finditer('(M[a-z]*o) (B[io][a-z]*) ([0-9]*)', elenco)
…

ultima.py

nome M[a-z]*o


cognomi doppi

import re

elenco='Luca Rossi 2345234 - ' \
       'Mario Bianchi 82342342 - ' \
       'Marco Verdi 342342342 - ' \
       'Roberto Biscardi 3243456 - ' \
       'Bianca Bugatti 343425 - ' \
       'Massimo Bologna Morelli 343245 - ' \
       'Giorgio Mona 3423424 - ' \
       'Maria Bissa 3234543'

i = re.finditer('(M[a-z]*o) (B[io][a-z]*) ([0-9]*)', elenco)
…

non stampa Massimo Bologna Morelli


cognomi doppi

       Massimo Bologna Morelli 343245
       ------- ------- -
          |       |    |
      M[a-z]*o    |  [0-9]*
             B[io][a-z]*

cognomi doppi: prima soluzione

il cognome è tutto Bologna Morelli

incluso lo spazio
e la M maiuscola


cognomi doppi: prima soluzione

import re

elenco='Luca Rossi 2345234 - ' \
       …
       'Massimo Bologna Morelli 343245 - ' \
       …

i = re.finditer('(M[a-z]*o) (B[io][A-Za-z ]*) ([0-9]*)', elenco)
…

doppi.py

anche spazio e lettera maiuscola successiva nel cognome


collimazione

       Massimo Bologna Morelli 343245
       ------- --------------- ------
          |           |          |
      M[a-z]*o        |        [0-9]*
               B[io][A-Za-z ]*

Bologna Morelli collima con B[io][A-Za-z ]*

       B o l o g n a  M o r e l l i
       | | ------------------------
       B |           |
       [io]          |
                 [A-Za-z ]*

parti opzionali

      abc?

collima con ab e con abc

a seguito da b, eventualmente seguito da c

? = la parte precedente può esserci o meno


seconda soluzione

import re

elenco='Luca Rossi 2345234 - ' \
       …
       'Massimo Bologna Morelli 343245 - ' \
       …

i = re.finditer('(M[a-z]*o) (B[io][A-Za-z]*( [A-Z][a-z]*)?) ([0-9]*)', elenco)
…

opzionale.py


collimazione

       Massimo Bologna Morelli 343245
          |       |       |      |
      M[a-z]*o    |       |    [0-9]*
          B[io][A-Za-z]*  |
                  ( [A-Z][a-z]*)?

" Morelli" collima con ( [A-Z][a-z]*)

spazio collima con spazio
M collima con [A-Z]
orelli collima con [a-z]*


stessa espressione, nomi singoli

       Mario Bianchi 82342342
          |       | ''  |
      M[a-z]*o    | | [0-9]*
     B[io][A-Za-z]* |
               ( [A-Z][a-z]*)?

( [A-Z][a-z]*) è seguita da ?
può anche non esserci
collima con la stringa vuota ''


gruppi

i = re.finditer('(M[a-z]*o) (B[io][A-Za-z]*( [A-Z][a-z]*)?) ([0-9]*)', elenco)
for m in i:
    print(m.group())
    print('    nome: ' + m.group(1))
    print('    cognome: ' + m.group(2))
    print('    telefono: ' + m.group(4))    # quarto gruppo, non terzo

opzionale.py

come si conteggiano i gruppi?


gruppi e sottogruppi

(M[a-z]*o) (B[io][A-Za-z]*( [A-Z][a-z]*)?) ([0-9]*)
---------- ------------------------------- --------
 gruppo 1      gruppo 2   ---------------  gruppo 4
                              gruppo 3

anche i gruppi dentro i gruppi vengono contati

il telefono ora è il quarto

se manca la parte opzionale, il gruppo 3 è None


cognomi multipli

elenco='Luca Rossi 2345234 - ' \
       'Mario Bianchi 82342342 - ' \
       'Marco Verdi 342342342 - ' \
       'Roberto Biscardi 3243456 - ' \
       'Bianca Bugatti 343425 - ' \
       'Massimo Bologna Morelli 343245 - ' \
       'Mirko Bosu Scinsani Basini 3435445 - ' \
       'Giorgio Mona 3423424 - ' \
       'Maria Bissa 3234543'

da uno a tre cognomi


prima soluzione

import re

elenco='Luca Rossi 2345234 - ' \
       'Mario Bianchi 82342342 - ' \
       'Marco Verdi 342342342 - ' \
       'Roberto Biscardi 3243456 - ' \
       'Bianca Bugatti 343425 - ' \
       'Massimo Bologna Morelli 343245 - ' \
       'Mirko Bosu Scinsani Basini 3435445 - ' \
       'Giorgio Mona 3423424 - ' \
       'Maria Bissa 3234543'

i = re.finditer('(M[a-z]*o) (B[io][A-Za-z]*( [A-Z][a-z]*)?( [A-Z][a-z]*)?) ([0-9]*)', elenco)
for m in i:
  print(m.group())
  print('    nome: ' + m.group(1))
  print('    cognome: ' + m.group(2))
  print('    telefono: ' + m.group(5))    # quinto gruppo

opzionali.py

un cognome

seguito da due, opzionali


conteggio dei gruppi

(M[a-z]*o) (B[io][A-Za-z]*( [A-Z][a-z]*)?( [A-Z][a-z]*)?) ([0-9]*)
---------- ---------------------------------------------- --------
 gruppo 1      gruppo 2   --------------- --------------  gruppo 5
                             gruppo 3        gruppo 4

anche i gruppi nei gruppi vanno contati


numero di ripetizioni

c{2,5} = sequenza, da due a cinque c


seconda soluzione

import re

elenco='Luca Rossi 2345234 - ' \
       'Mario Bianchi 82342342 - ' \
       'Marco Verdi 342342342 - ' \
       'Roberto Biscardi 3243456 - ' \
       'Bianca Bugatti 343425 - ' \
       'Massimo Bologna Morelli 343245 - ' \
       'Mirko Bosu Scinsani Basini 3435445 - ' \
       'Giorgio Mona 3423424 - ' \
       'Maria Bissa 3234543'

i = re.finditer('(M[a-z]*o) (B[io][A-Za-z]*( [A-Z][a-z]*){0,2}) ([0-9]*)', elenco)
…

multipli.py


numerazione dei gruppi

i = re.finditer('(M[a-z]*o) (B[io][A-Za-z]*( [A-Z][a-z]*){0,2}) ([0-9]*)', elenco)
for m in i:
    print(m.group())
    print('    nome: ' + m.group(1))
    print('    cognome: ' + m.group(2))
    print('    telefono: ' + m.group(4))    # quarto gruppo, non terzo

basta contare il numero di parentesi aperte


gruppi ripetuti

(M[a-z]*o) (B[io][A-Za-z]*( [A-Z][a-z]*){0,2}) ([0-9]*)
---------- ----------------------------------- --------
 gruppo 1      gruppo 2   -------------------  gruppo 4
                               gruppo 3

conteggio parentesi

nel gruppo 3 va solo l'ultima ripetizione di ( [A-Z][a-z]*)
esempio: Mirko Bosu Scinsani Basini 3435445:
la sottoespressione collima con Scinsani Basini
solo Basini va nel gruppo 3


elenco telefonico su file

import re

file = open("elenco.txt")
elenco = file.read()
i = re.finditer('(M[a-z]*o) (B[io][A-Za-z]*( [A-Z][a-z]*){0,2}) ([0-9]*)', elenco)
for m in i:
    print(m.group())
    print('    nome: ' + m.group(1))
    print('    cognome: ' + m.group(2))
    print('    telefono: ' + m.group(4))

file.py
elenco.txt

stesso programma
elenco telefonico letto da file di testo


esercizio: date

stampare le date nel file testo.txt

le date sono nel formato 21/11/2013


data: espressione regolare

due cifre, barra, due cifre, barra, quattro cifre

[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]

data: programma

import re

s = open('testo.txt').read()
i = re.finditer('[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]', s)
for m in i:
    print(m.group())

data.py

non trova 3/1/2012


variante con lunghezza arbitraria

[0-9]*/[0-9]*/[0-9][0-9][0-9][0-9]

arbitraria.py

trova 3/1/2012

ma anche 1000/2000/3000


lunghezza delle sequenze

*
lunghezza qualsiasi
{1,3}
lunghezza da uno a tre

date, con lunghezza uno o due

[0-9]{1,2}/[0-9]{1,2}/[0-9]{4,4}

unodue.py

[0-9]{1,2} = una o due cifre

[0-9]{4,4} = quattro cifre

{4,4} si può anche scrivere {4}

invece: [0-9]* era una sequenza di cifre di lunghezza qualsiasi (anche zero)


date: confronto con le stringhe

            12/03/2002
            -- -- ----
	    ↑   ↑    ↑
     +------+   |    +---+
     |          |        |
---------- ---------- --------
[0-9]{1,2}/[0-9]{1,2}/[0-9]{4}

12 è [0-9] ripetuto due volte

              3/1/2012
              - - ----
	      ↑ ↑   ↑
     +--------+ |   +----+
     |          |        |
---------- ---------- --------
[0-9]{1,2}/[0-9]{1,2}/[0-9]{4}

3 è [0-9] ripetuto una volta


esempio: codice fiscale

stampare i codici fiscali nel file fiscali.txt


espressione regolare

espressione='[A-Z]{3} [A-Z]{3} [0-9]{2}[A-Z][0-9]{2} [A-Z][0-9]{3}[A-Z]'

programma

import re

espressione='[A-Z]{3} [A-Z]{3} [0-9]{2}[A-Z][0-9]{2} [A-Z][0-9]{3}[A-Z]'

s = open('fiscali.txt').read()
i = re.finditer(espressione, s)
for m in i:
    print(m.group())

fiscali.py

non trova LHTRFG54T32I123N
codice fiscale scritto senza spazi


spazi

spazio opzionale: [ ]{0,1}

[A-Z]{3}[ ]{0,1}[A-Z]{3}…

[ ] contiene un singolo carattere
le quadre non servono

[A-Z]{3} {0,1}[A-Z]{3}…
        ↑
    (spazio)

spazi: espressione regolare e programma

[A-Z]{3} {0,1}[A-Z]{3} {0,1}[0-9]{2}[A-Z][0-9]{2} {0,1}[A-Z][0-9]{3}[A-Z]

zerouno.py


spazi opzionali

{0,1} è uguale a ?

spazio opzionale:

[A-Z]{3} ?[A-Z]{3}…
        ↑
    (spazio)

interrogativo.py


ritorni a capo

fiscali.txt

ancora non si riesce a trovare l'ultimo codice fiscale:

un codice fiscale puo' venire per caso spezzato da un ritorno a capo, come DRT
HAR 21S34 C243X

ritorno a capo invece di uno spazio


carattere di ritorno a capo

è un singolo carattere

in molti linguaggi di programmazione: \n

si può inserire in una stringa qualsiasi


ritorno a capo in una stringa

a = 'una stringa che contiene\nun ritorno a capo'
print(a)

stringa.py

una stringa che contiene
un ritorno a capo

n = lettera enne
\n = ritorno a capo


ritorno a capo per i codici fiscali

[A-Z]{3}[ \n]?[A-Z]{3}[ \n]?[0-9]{2}[A-Z][0-9]{2}[ \n]?[A-Z][0-9]{3}[A-Z]

capo.py

ritorno a capo \n
in alternativa allo spazio [ \n]
sempre opzionale ?


punto e a capo

[A-Z]{3}.[A-Z]{3}.[0-9]{2}[A-Z][0-9]{2}.[A-Z][0-9]{3}[A-Z]

nodot.py

il punto è un carattere qualsiasi
invece dello spazio o del ritorno a capo

non trova il codice fiscale su due linee


re.DOTALL

import re

espressione='[A-Z]{3}.[A-Z]{3}.[0-9]{2}[A-Z][0-9]{2}.[A-Z][0-9]{3}[A-Z]'

s = open('fiscali.txt').read()
i = re.finditer(espressione, s, re.DOTALL)
for m in i:
    print(m.group())

dot.py

normalmente, il punto non include il ritorno a capo

con re.DOTALL lo include


riassunto

*
lunghezza qualsiasi
?
lunghezza zero oppure uno
{2}
lunghezza due
{4,}
da quattro in su
{1,3}
da uno a tre
\n
ritorno a capo
re.DOTALL
il punto include il ritorno a capo

date con i nomi dei mesi

21 gennaio 2013
4 luglio 2002
14 agosto 2014

una o due cifre, un nome di mese, quattro cifre

impossibile con le cose viste finora


nomi dei mesi con le quadre

[0-9]{1,2} [gennaiofebbraiomarzoaprile…] [0-9]{4}

non funziona

è un carattere fra g, e, n, a, ecc.


nomi dei mesi con le sequenze

[0-9]{1,2} [gennaiofebbraiomarzoaprile…]* [0-9]{4}

non funziona

è una sequenza di caratteri qualsiasi fra quelli

prende anche 2 per 1000


alternative

gennaio|febbraio|marzo|aprile

alternativa tra stringhe intere

accetta:

gennaio
febbraio
marzo
aprile

nomi dei mesi: espressione regolare

[0-9]{1,2} (gennaio|febbraio|marzo|aprile|maggio|giugno|luglio|agosto|settembre|ottobre|novembre|dicembre) [0-9]{4}

mesi.py

parentesi: altrimenti sarebbe una scelta fra:


alternativa e classi

[abc] è uguale a (a|b|c)

le classi […] si possono usare solo per i singoli caratteri

sono una forma limitata di alternativa

vantaggio: più brevi; permettono a-z ecc.


alternativa, in generale

espressione|espressione|espressione

le espressioni possono essere qualsiasi

[0-9]*|[a-z]*

una sequenza di sole cifre oppure una sequenza di sole minuscole


nidificazione delle espressioni regolari

due formati per la data:

invece: 02/dicembre/2013 non è ammesso

sono due formati alternativi


date: alternative

[0-9]{2}/[0-9]{2}/[0-9]{4}|[0-9]{1,2} (gennaio|febbraio|…) [0-9]{4}

alternative.py

è una alternativa tra due espressioni regolari:

[0-9]{2}/[0-9]{2}/[0-9]{4}
[0-9]{1,2} (gennaio|febbraio|…) [0-9]{4}

nidificazione: codice fiscale

dato che [A-Z]{3} si ripete due volte:

([A-Z]{3} ){2}[0-9]{2}[A-Z][0-9]{2} [A-Z][0-9]{3}[A-Z]

l'intera espressione regolare ([A-Z]{3} ) ripetuta due volte


numeri

trovare i numeri in numeri.txt


numeri, con asterisco

import re

espressione='[0-9]*'

s = open('numeri.txt').read()
i = re.finditer(espressione, s)
for m in i:
    print('numero:', m.group())

numeri.py

stampa:

numero:
numero:
numero:
numero:
…
numero:  234
numero:
numero:
numero:
…

[0-9]* collima anche con la stringa vuota


prima cifra obbligatoria

[0-9][0-9]*

prima.py

prima una cifra
poi una cifra ripetuta un numero qualsiasi di volte


da uno a qualsiasi

[0-9]{1,}

uno.py

secondo numero assente in {1,}
uguale qualsiasi


più

[0-9]+

piu.py

+ = numero qualsiasi di volte tranne zero


differenza tra * e +

* è uguale a {0,}
da zero a qualsiasi ripetizioni

+ è uguale a {1,}
da uno a qualsiasi ripetizioni


somme

stampare le somme

somma: intero+intero
solo due interi, per ora


interi e somme di interi

un intero: [0-9]+

una somma tra due interi: [0-9]{1,}[+][0-9]{1,}

somma.py

il carattere di somma va tra quadre
altrimenti è la ripetizione da una a qualsiasi volte


somme multiple

anche somme di tre interi


ripetizione della somma

[0-9]{1,}[+][0-9]{1,}
---------   ---------
 intero   +  intero

la sequenza +intero va ripetuta
almeno una volta

[0-9]{1,}([+][0-9]{1,})+
--------- ------------
  intero     +intero
         ---------------
	 +intero+intero+intero...

somme.py


altre operazioni

somme, sottrazioni, moltiplicazioni, divisioni

anche mescolate fra loro


scelta

[0-9]{1,}([+-*/][0-9]{1,})+

operazioni.py

vari errori


trattino in un intervallo

+-*/ significa alternativa fra

+-*
un carattere fra + e *
/ carattere /

trattino alla fine dell'intervallo

[0-9]{1,}([+*/-][0-9]{1,})+

trattino.py

subito prima della quadra chiusa
il trattino è il carattere trattino


riassunto

[…]
scelta fra singoli caratteri
* + {n,m}
ripetizioni
?
opzionalità
|
alternativa
(…)
gruppo

tutti applicabili a espressioni complesse tranne […]


nomi dei file

trovare nomi come elenco.txt o numeri.txt

dove vanno cercati: filetesto.txt

il file elenco.txt contiene un primo esempio mentre testo.txt, fiscali.txt e
numeri.txt sono altri esempi

estensione del file

estensione di elenco.txt:
solo la parte finale txt

.*[.]txt

sequenza che termina con .txt

file: il file elenco.txt contiene un primo esempio mentre testo.txt, fiscali.txt
file: numeri.txt

collimazioni alternative

più corta:

il file elenco.txt contiene un primo esempio mentre testo.txt, fiscali.txt e
              | |
--------------|txt
      .*      |
             [.]

più lunga:

il file elenco.txt contiene un primo esempio mentre testo.txt, fiscali.txt e
                             |                                        | |
----------------------------------------------------------------------|txt
                            .*                                        |
                                                                     [.]

intermedia:

il file elenco.txt contiene un primo esempio mentre testo.txt, fiscali.txt e
                            |                            |
---------------------------------------------------------|txt
                            .*                           |
                                                        [.]

collimazione più lunga

viene sempre scelta la collimazione più lunga


soluzione

[^ \n]*[.]txt

lunga.py

il nome del file non include lo spazio
e nemmeno il ritorno a capo

file: elenco.txt
file: testo.txt
file: fiscali.txt
file: numeri.txt

maiuscole e minuscole

vengono considerate diverse:

if re.search('a', 'rsAmoe'):
  print 'trovato'

non trova niente, invece:

if re.search('a', 'rsAmoe', re.IGNORECASE):
  print 'trovato'

trova la maiuscola


ma il terzo argomento…

non era per re.DOTALL?

si possono anche mettere tutti e due:

if re.search('a', 'fsAsdf', re.DOTALL | re.IGNORECASE):
  print 'trovato'

l'operatore | è l'OPPURE sui singoli bit


opzioni multiple

sistema molto usato per passare più opzioni:


valori delle opzioni

print('re.DOTALL:', re.DOTALL+1-1)
print('re.IGNORECASE:', re.IGNORECASE+1-1)

numeri 16 e 2


operazione bit per bit

re.DOTALL|re.IGNORECASE
  16 = 10000
   2 = 00010
16|2 = 10010 = 18

cosa fanno search() e finditer()

terzo argomento flags

se flags&re.DOTALL vale 1
include \n nel .
se flags&re.IGNORECASE vale 1
considera maiuscole e minuscole uguali

come funziona la decodifica dei flag

default: flags=0
flags&re.DOTALL= 00000 & 00010
non include \n nel .

se flags=re.DOTALL|re.IGNORECASE:
flags&re.DOTALL = 10010 & 00010


codifica e decodifica

re.DOTALL|re.IGNORECASE = 10000 | 00010 = 10010
                                                & 00010

| aggiunge un bit
& seleziona solo quello


inizio e fine stringa

re.search(espressione, stringa)
re.finditer(espressione, stringa)

trovano una slice all'interno della stringa

può essere all'inizio, in mezzo o in fondo

si può specificare che deve essere all'inizio o alla fine


inizio stringa

re.search('Mario', elenco)
trova Mario in posizione qualsiasi
re.search('^Mario', elenco)
trova Mario solo se è all'inizio

esempio:

re.search('a', 'casupola') ha successo

re.search('^a', 'casupola') no
(la a c'è ma non all'inizio)


fine stringa

come ^, ma con $


inizio e fine: esempi

m=re.search('a+$', 'aaa bb ccc aaxaa zz aaaaa')
print m.group()

trova solo la sequenza di a in fondo

i=re.finditer('^a', 'aaaanbb sdaaaa  aaa')
for m in i:
  print m.group()

stampa solo a

ci sono altre a, ma non all'inizio


stringa intera

es: nome

import re

m = re.search('^[A-Z][a-z]+$', s)
if m:
  print 'nome'

ha successo solo se il nome è tutta la stringa s


esempi


inizio e fine di una parola

trovare tutti gli articoli 'il' in testo.txt

primo tentativo: parola

import re

s = open('testo.txt').read()

print("espressione 'il':")
i = re.finditer('il', s)
for m in i:
    print(m.group())

risultato:

espressione 'il':
il
il
il

trova anche 'il' in 'possibile'

include anche gli spazi


secondo tentativo: spazi

import re

s = open('testo.txt').read()

print("espressione ' il ':")
i = re.finditer(' il ', s)
for m in i:
    print(m.group())

risultato:

espressione ' il ':
 il

non trova 'il' all'inizio della riga

include anche gli spazi


soluzione: delimitatore di parola

import re

s = open('testo.txt').read()

print("espressione r'\\bil\\b':")
i = re.finditer(r'\bil\b', s)
for m in i:
    print(m.group())

risultato:

espressione r'\bil\b':
il
il

parola.py


cosa fa il delimitatore

import re

s = open('testo.txt').read()

print("espressione r'\\bil\\b':")
i = re.finditer(r'\bil\b', s)
for m in i:
    print(m.group())

risultato:

espressione r'\bil\b':
il
il

\b collima solo con inizio e fine di parola
spazio, inizio o fine riga

lo spazio prima della parola non va nel gruppo
nemmeno quello dopo
devono esserci, ma non vengono "consumati"

senza r la \b sarebbe un carattere speciale
invece del delimitatore di parola


espressioni che non consumano caratteri

^ e $ sono nell'espressione regolare
richiedono la presenza di qualcosa nella stringa (inizio e fine)
ma non vanno nel gruppo

\b inizio o fine di una parola
richiede che ci sia prima un inizio di stringa o uno spazio
ma non va nel gruppo


solo una questione di gruppo?

includere anche la parola dopo 'il'


parola dopo l'articolo

import re

s = open('testo.txt').read()

print("espressione r'\\bil\\b':")
i = re.finditer(r'\bil\b[a-z0-9]*', s)
for m in i:
    print(m.group())

risultato:

espressione r'\bil\b[a-z0-9]*':
il
il

non trova la parola


cosa ha fatto il delimitatore

import re

s = open('testo.txt').read()

print("espressione r'\\bil\\b':")
i = re.finditer(r'\bil\b[a-z0-9]*', s)
for m in i:
    print(m.group())

risultato:

espressione r'\bil\b[a-z0-9]*':
il
il

\bil\b collima con 'il'
non con ' il '

c'è ancora uno spazio, prima delle cifre


includere lo spazio

import re

s = open('testo.txt').read()

print("espressione r'\\bil\\b':")
i = re.finditer(r'\bil\b [a-z0-9]*', s)
for m in i:
    print(m.group())

risultato:

espressione r'\bil\b [a-z0-9*':
il giorno
il 3

\bil\b collima con 'il'
segue uno spazio
segue la parola successiva

consumo.py


altre operazioni con espressioni regolari

split
divide una stringa
sub
sostituisce sottostringhe

split

divide una stringa in parti

l'espressione regolare dice dove separare

es: [;|] dice che ; e | fungono da separatori

p = re.compile('[;|]')
print p.split('abcd;efgh|wsl')

stampa:

['abcd', 'efgh', 'wsl']

sub

rimpiazza le sottostringhe che collimano con l'espressione regolare

p = re.compile('(Giorgio|Luca|Alberto)')
print p.sub('Gianni', "Questo l'ha fatto Giorgio, questo Alberto, questo Luca")

stampa:

Questo l'ha fatto Gianni, questo Gianni, questo Gianni

altri usi delle espressioni regolari


programmi di ricerca e manipolazione testo

esempio:

sed 's/^\([a-z]*\)-\([a-z]*\)/\2-\1/'

rimpiazza abcd-xyz con xyz-abcd

\(…\) indicano i gruppi

nel rimpiazzamento \2-\1, i numeri \2 e \1 indicano i gruppi nella stringa


interprete da linea di comando

command.com in DOS e Windows
(prompt dei comandi)

/bin/bash e simili in UNIX

usano forme limitate di espressioni regolari

ls a*		(bash)
dir a*.*	(command)

si usa * al posto di .*
indica una ripetizione di caratteri qualsiasi

visualizza in nomi dei file che iniziano con a


funzione di ricerca negli editor di testo

esempio: google docs


campi di un modulo

all'interno di una pagina html5:

<input type="text" title="una data"
 pattern="[0-9]{1,2}/[0-9]{1,2}/[0-9]{4}" />

effetto:

inserire una data:

tab o pulsante prova:
verifica che nel campo ci sia una data


teoria delle espressioni regolari

modello matematico

si guarda l'intera stringa
come ^espressione$


alfabeto e stringa


notazione

se l'alfabeto è A:

An è l'insieme di tutte le stringhe di lunghezza n fatte di simboli di A
A* è l'insieme di tutte le stringhe fatte di simboli di A

operazioni su stringhe

concatenazione
s·r i simboli di s e poi i simboli di r

insiemi di stringhe

insieme vuoto
unione
A∪B tutte le stringhe di A e tutte quelle di B
intersezione
A∩B solo le stringhe che sono sia in A che in B

espressione regolare

una espressione regolare è un modo per specificare un insieme di stringhe

dato un alfabeto A, una espressione regolare lo divide in:


sintassi delle espressioni regolari

dato un alfabeto:


non vengono considerati

nella trattazione teorica:

.
un carattere qualsiasi:
è uguale a (a|b|…)
[ ]
si usa |
{n,m}
si usano concatenazione, | e ?
es: a{2,4} è aaa?a?
+
si usa *
es: a+ è aa*

stringa vuota

ε indica la stringa vuota

esempio:

	ε|e = e?

? si può definire in termini di ε |


semantica

espressione regolare → insieme di stringhe che collimano

la semantica è una funzione matematica collimano()

data una espressione regolare e:

collimano(e) = insieme di stringhe che collimano con l'espressione regolare

semantica: esempi

definizione generale di collimano?


semantica, in generale

definire collimano per ogni espressione regolare

modo "automatico" per dire quanto vale collimano(e)

sistema: seguire la definizione sintattica


semantica, dalla sintassi

la sintassi era:

una espressione regolare è: un carattere, oppure ε, oppure …

vari casi

definire collimano in ognuno


sintassi ↔ semantica

possibile
espressione
regolare
semantica
εcollima(ε)=?
x ∈ alfabetocollima(x)=?
efcollima(ef)=?
e*collima(e*)=?
e|fcollima(e|f)=?
e?collima(e?)=?

in ognuno dei casi, definire collima

deve produrre le stringhe che collimano


semantica: casi facili

l'unica stringa che collima con ε è la stringa vuota

collima(ε)={∅}

l'unica stringa che collima con l'espressione regolare x è la stringa composta solo dal carattere x

collima(x)={x}

concatenazione

se è una concatenazione di caratteri:

collima(xy)={xy}

in generale?

es: a*b? è la concatenazione di a* e b?
cosa c'è in collima(a*b?)?


definizione ricorsiva della semantica

problema: definire collima(a*b?)

ma a* è una espressione regolare

anche b?

quindi collima sarà definito anche per loro


semantica delle composizioni

collima(ef)
insieme delle stringhe ottenute concatenando una stringa di collima(e) con una di collima(f)
collima(e*)
insieme delle stringhe ottenute concatenando un numero qualsiasi di stringhe di collima(e)
collima(e|f)
collima(e) ∪ collima(f)
(unione)
collima(e?)
{∅} ∪ collima(e)

definizione: funziona?

a prima vista: collima è definito in termini di se stesso

problema dell'uovo e della gallina

in realtà no

non in questo caso


niente uovo e gallina

gallina → uovo → gallina → uovo → …

il problema è che da gallina si torna a gallina

con le espressioni regolari no!

collima(a*b?) → collima(a*) collima(b?)
                     ↓         ↓
                 collima(a)  collima(b)

non ci sono cicli


definizioni ricorsive

collima è definito in termini di se stesso?

non esattamente:

collima(e) è definito in termini di collima(f), collima(g), ecc. dove f e g sono più semplici di e

es. ef definito in termini di e e di f
e* definito in termini di e, ecc.


sviluppo della definizione

collima(e) è definito in termini di collima(f) ecc.,
più semplici

collima(f) è definito in termini di collima(g), ecc.
ancora più semplici

ecc.

da collima(e) prima o poi si arriva a collima(ε) e collima(x)