Espressioni Regolari - UniCT · In python 3 è il comportamento di default, quindi non è...

Post on 10-Jul-2020

7 views 0 download

Transcript of Espressioni Regolari - UniCT · In python 3 è il comportamento di default, quindi non è...

Python Standard LibraryEspressioni Regolari

Espressioni Regolari

› Un'espressione regolare (regular expression o regex) è una sequenza di caratteri che identifica un insieme di stringhe.

› Sono un potente strumento che consente di trovare, estrarre e sostituire parti di testo all'interno di un documento.

› Benché siano state formalizzate dal punto di vista puramente matematico fin dagli anni quaranta, è con l'avvento dei sistemi UNIX che comparirono i primi tool basati sulle espressioni regolari (tra cui un precursore dell'attuale grep).

Esempi di regex

[-+]?[0-9]+

Trova tutti i numeri interi con segno.

^.*\.txt$

Trova tutti i file che hanno estensione txt.

\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}

Trova gli indirizzi IPv4.

^4[0-9]{12}(?:[0-9]{3})?$

Trova numeri di carte di credito.

Le regex più semplici

› Nella loro forma più semplice, una regex è una sequenza di caratteri alfanumerici.

› abc ad esempio, trova la stringa 'abc'.

› Le espressioni regolari, di norma, sono case sensitive. La regex di prima infatti troverà la stringa 'abc', ma non la stringa 'Abc' o 'aBc'.

› Le regex possono, tramite regole opportune, trovare qualsiasi carattere, compresi gli altri caratteri ASCII ('~', '|') e persino quelli Unicode ('è', '①', '⼦').

› Bisogna tuttavia porre attenzione al fatto che alcuni caratteri hanno una funzione particolare nel linguaggio delle regex.

Notazione

› Le espressioni regolari saranno sempre indicate in carattere monospaced.

› Per indicare che una regex trova un determinato insieme di stringhe verrà utilizzato il simbolo →.

› L'insieme di stringhe trovate da una regexseguono la consueta notazione insiemistica. Un insieme illimitato è delimitato dai puntini di sospensione.

abc → {'abc'}

Quantificatore *

*

Detto anche stella di Kleene, serve per trovare famiglie di stringhe che hanno da 0 a N ripetizioni di uno stesso carattere.

ab*c → {'ac', 'abc', 'abbc', 'abbbc', …}

I quantificatori si riferiscono sempre al carattere immediatamente precedente.

Quantificatori + e ?

+

Identico a *, ma le ripetizioni ammesse sono da 1 a N, e non da 0 a N.

ab+c → {'abc', 'abbc', 'abbbc', …}

?

Ammette l'opzionalità del carattere precedente (può essere presente 0 o 1 volta).

ab?c → {'ac', 'abc'}

Operatore \

\

Effettua l'escaping del carattere precedente, se esso è un carattere particolare.

a** → Non valida

a\** → {'a', 'a*', 'a**', 'a***', …}

a*\* → {'*', 'a*', 'aa*', 'aaa*', …}

a\*\* → {'a**'}

a\c → Non valida

a\\c → {'a\c'}

Gruppi

› In realtà non è vero che gli operatori si applicano al carattere precedente: è più corretto dire che si applicano al token precedente.

› Un token, nel caso più semplice, è un carattere, ma può essere anche un gruppo o una classe di caratteri.

› In particolare, un gruppo di caratteri è una stringa delimitata dalle parentesi tonde.

a(bc)*d → {'ad', 'abcd', 'abcbcd', …}

› Nell'esempio precedente, il token su cui è applicata la stella di Kleene è (bc).

› L'ordine dei caratteri all'interno del gruppo è importante!

a(cb)*d → {'ad', 'acbd', 'acbcbd', …}

Operatore|

Specifica alternative all'interno di un gruppo.

gr(a|e)y → {'gray', 'grey'}

In un gruppo possono essere presenti più alternative in qualsiasi ordine, e le alternative sono stringhe di qualsiasi lunghezza.

(cla|repl|gu)y → {'clay', 'reply', 'guy'}

È inoltre possibile specificare la stringa vuota come alternativa:

(na|)y → {'y', 'nay'}

Classi

› Esiste un modo più pratico per trovare alternative di singoli caratteri, ossia definire una classe di caratteri.

› Una classe di caratteri è un insieme non ordinato di caratteri in alternativa tra di loro delimitato da parentesi quadre.

› La regular expression gr(a|e)y può essere dunque riscritta come:

gr[ae]y → {'gray', 'grey'}

Classi

› I quantificatori visti finora perdono di significato se sono inseriti all'interno di una classe, eccetto l'operatore \.

a[-+*\\]b → {'a+b', 'a-b', 'a*b', 'a\b'}

› Una classe di caratteri è anche essa un token su cui è possibile applicare un quantificatore.

a[cd]?b → {'ab', 'acb', 'adb'}

› Lo spazio è un carattere ammesso.

a[ _]b → {'a_b', 'a b'}

Intervalli

Il carattere - definisce un intervallo di caratteri all'interno di una classe.

Esso è utilizzabile solamente per definire intervalli di caratteri alfanumerici, sia uppercase che lowercase: se usato singolarmente si comporta come un normale carattere.

1[0-4] → {'10', '11' , '12' , '13' , '14'}

[A-Za-z]* → una qualsiasi sequenza di lettere

[+-]?17 → {'17', '+17' , '-17'}

Negazione

Il carattere ^, se posto all'inizio di una classe, specifica che la classe rappresenta i caratteri che NON devono essere presenti.

Perde questo significato se è non è il primo carattere di una classe.

1[^0-4] → {'15', '16' , '1a' , '1T' , …}

a[&^|]b → {'a&b', 'a^b' , 'a|b'}

Classi speciali

.

Trova qualsiasi carattere eccetto il newline '\n'.

. → {'a', 'b' , '4' , '&' , …}

\d

Trova caratteri numerici. Include [0-9] ma anche tutti i caratteri non-ASCII riconducibili a numeri.

\d → {'0', '1' , '2' , '3' , '𝟘', …}

Classi speciali

\s

Trova whitespace. Include [ \t\n\r\f\v] e tutti i caratteri whitespace speciali Unicode.

\w

Trova caratteri alfanumerici. Include [A-Za-z0-9_] ma anche tutti i caratteri non-ASCII riconducibili a numeri o lettere.

\w → {'a', 'T' , '9' , 'è' , '①', '_', …}

Classi speciali

› Esistono anche le classi duali di \d, \w, e \s, e sono rispettivamente \D, \W, \S.

› Esse trovano esattamente i caratteri complementari alle classi base:– \D trova tutti i caratteri che non sono numeri.

– \W trova tutti i caratteri che non sono numeri né lettere.

– \S trova tutti i caratteri non whitespace.

\W → {'#', '[' , '+' , '.' , '€', …}

Classi speciali

› Le classi speciali \d, \w, e \s, e le loro duali possono essere utilizzate all'interno di altre classi.

[\daB] → {'0', '1' , '2' , … , 'a', 'B'}

› Il punto all'interno di una classe invece vale sempre e solo come carattere, non come classe.

[^.] → tutti i caratteri tranne '.'

Quantificatori greedy e lazy

› Di norma, i quantificatori sono sempre greedy, ossia cercano di trovare quanti più caratteri possibili.

› A volte questo comportamento non è quello desiderato.

› Immaginiamo di volere estrarre il nome del tag dal seguente frammento di HTML:

<div>Some paragraph</div>

Quantificatori greedy e lazy

<div>Some paragraph</div>

Regex pattern: <.*>

› Sappiamo che il punto trova qualsiasi carattere eccetto il newline.

› Il problema è che anche '>' è un carattere! È quindi perfettamente logico che venga incluso nella stringa trovata.

Quantificatori greedy e lazy

› La soluzione al problema è rendere il quantificatore lazy, facendogli catturare la minorquantità di caratteri possibili che soddisfano l'espressione regolare.

› Per specificare che un quantificatore è lazy si usa il punto interrogativo.

<div>Some paragraph</div>

Regex pattern: <.*?>

Ancore

› Quando si ricerca una pattern all'interno di una stringa, a volte è utile specificare esplicitamente dove questa pattern deve essere collocata all'interno del testo.

hear ya, hear ya

Regex pattern: hear

› Esistono degli operatori, chiamati ancore (anchors), che vincolano la posizione delle sequenze di caratteri trovate.

Ancore

^

Vincola la posizione della sequenza di caratteri trovata all'inizio della stringa.

hear ya, hear ya

Regex pattern: ^hear

$

Vincola la posizione della sequenza di caratteri trovata alla fine della stringa.

hear ya, hear ya

Regex pattern: ya$

Modulo re

https://docs.python.org/3/library/re.html

Python permette, tramite il suo modulo re, di utilizzare il motore delle espressioni regolari.

re.search

re.search(pattern, string, flags=0)

Cerca in string la pattern regex specificata.

flags è un intero che modifica il comportamento della funzione.

Il suo valore di ritorno è un Match object.

>>> html = '<div>Another paragraph</div>'

>>> re.search('<.*?>', html)

<_sre.SRE_Match object at 0x1ab06a0>

Match object

Contengono varie informazioni sulle stringhe trovate.

Sono convertibili automaticamente al booleano True:

match = re.search(pattern, string)

if match:

process(match)

I match object possono essere usati per accederealle stringhe trovate corrispondenti ai vari gruppi.

match.group

match.group([group1, …])

Restituisce una o più stringhe trovate corrispondenti a uno o più gruppi.

Chiamata senza argomenti è equivalente a chiamare match.group(0).

Se uno dei groupN è 0, il valore corrispondente sarà l'intera stringa trovata.

Se vengono passati più gruppi come parametri, restituisce una tupla contenente tutte le stringhe relative a quei gruppi.

Esempio

>>> import re

>>> text = "Isaac Newton, physicist"

>>> m = re.match(r"(\w+) (\w+)", text)

>>> m.group()

'Isaac Newton'

>>> m.group(1)

'Isaac'

>>> m.group(0, 2)

('Isaac Newton', 'Newton')

Raw strings

› Per evitare di inserire troppi caratteri di escape, in python è possibile dichiarare una stringa come rawstring.

› Le raw string si dichiarano mettendo la lettera rprima della stringa.

>>> r'Hello!\n'

'Hello!\\n'

match.groups

match.groups(default=None)

Restituisce una tupla contenente tutte le stringhe trovate dai vari gruppi.

default è usato quando un gruppo opzionale non è presente nella stringa d'origine.

>>> import re

>>> m = re.match(r"(\d+)\.?(\d+)?", "24")

>>> m.groups()

('24', None)

>>> m.groups('0')

('24', '0')

re.sub

re.sub(pattern, repl, string, count=0, flags=0)

Sostituisce in string la pattern regex specificata con la stringa repl fino ad un massimo di count volte.

Se count è 0 sostituisce tutte le occorrenze di pattern con repl.

flags è un intero che modifica il comportamento della funzione.

>>> html = '<div>Another paragraph</div>'

>>> re.sub('(</|<).*?>', r'\1span>', html)

' <span>Another paragraph</span>'

Backreference

La \1 nell'esempio è chiamata backreference.

Viene sostituita durante l'elaborazione con la stringa trovata dalla pattern specificata nel primo gruppo.

\1 assumerà il valore corrispondente alla stringa che viene trovata dalla pattern del primo gruppo.

<div>Another paragraph</div>

Regex pattern: (</|<).*?>

Match: <div> ⇒ \1 = '<'

Match: </div> ⇒ \1 = '</'

Se sono definiti più gruppi, è possibile usare la backreference \i (ove i è un numero) per ricavare ilvalore dell'i-esimo gruppo.

re.split

re.split(pattern, string, maxsplit=0, flags=0)

Divide string in frammenti usando come divisore pattern, creando al massimo maxsplit frammenti.

Se maxsplit è 0 invece divide la stringa il più possibile.

flags è un intero che modifica il comportamento della funzione.

Restituisce una lista di stringhe corrispondenti ai frammenti.

Esempio

>>> import re

>>> text = 'Words, words, words.'

>>> re.split('\W+', text)

['Words', 'words', 'words', '']

>>> re.split('\W+', text, 1)

['Words', 'words, words.']

Alcune flag

re.DOTALL (re.S)

Il . trova anche i caratteri newline '\n'.

re.MULTILINE (re.M)

La ricerca viene effettuata su ogni linea della stringa, non una volta sola.

re.IGNORECASE (re.I)

Ignora il case delle lettere presenti in pattern.

Alcune flag

re.UNICODE (re.U)

Le classi \w, \d, \s e duali trovano, oltre ai caratteri ASCII corrispondenti, anche eventuali caratteri Unicode equivalenti.

! In python 3 è il comportamento di default, quindi non è necessario specificarlo.

re.ASCII (re.A)

Le classi \w, \d, \s e duali trovano solamente caratteri ASCII.

! In python 2 è il comportamento di default, e questa flag non esiste.

re.compile

re.compile(pattern, flags=0)

È possibile compilare una pattern in un oggetto regex. Questo è utile se si pensa di riutilizzare la stessa regex pattern più di una volta.

flags ha lo stesso significato dei metodi

Sull'oggetto regex è possibile chiamare i metodi search, sub e split.

>>> import re

>>> regex = re.compile(pattern)

>>> regex.search(text)

Esercizio: iwget

› iwget è un'utilità a riga di comando che scarica tutte le immagini di una pagina web.

› Per farlo dobbiamo compiere i seguenti passi:

1. Accettare l'URL della pagina web da riga di comando.

2. Scaricare la pagina (X)HTML corrispondente all'URL.

3. Leggere il contenuto di tutti gli attributi src delle tag img, tenendo presente che l'(X)HTML non sempre è ben formato.

4. Per ogni match, scaricare l'immagine in una qualsiasi cartella locale.

Esercizio: iwget

› Per svolgere questo esercizio abbiamo bisogno di una libreria che consente di scaricare pagine web.

› Non è tuttavia parte del corso spiegare come utilizzarla.

› La soluzione? Documentazione ufficiale!