Python Language
Espressioni regolari (Regex)
Ricerca…
introduzione
Python rende disponibili le espressioni regolari attraverso il modulo re
.
Le espressioni regolari sono combinazioni di caratteri che vengono interpretate come regole per la corrispondenza delle sottostringhe. Ad esempio, l'espressione 'amount\D+\d+'
corrisponderà qualsiasi stringa composta dalla parola amount
più un numero intero, separati da uno o più non cifre, ad esempio: amount=100
, amount is 3
, amount is equal to: 33
, ecc.
Sintassi
Espressioni regolari dirette
re.match (pattern, stringa, flag = 0) # Out: combina pattern all'inizio della stringa o None
re.search (pattern, string, flag = 0) # Out: combina pattern all'interno di string o None
re.findall (pattern, stringa, flag = 0) # Out: lista di tutte le corrispondenze di pattern in stringa o []
re.finditer (pattern, string, flag = 0) # Out: uguale a re.findall, ma restituisce l'oggetto iteratore
re.sub (modello, sostituzione, stringa, flag = 0) # Out: stringa con sostituzione (stringa o funzione) al posto del modello
Espressioni regolari precompilate
precompiled_pattern = re.compile (pattern, flag = 0)
precompiled_pattern.match (stringa) # Out: corrisponde all'inizio della stringa o None
precompiled_pattern.search (stringa) # Out: corrisponde ovunque in stringa o None
precompiled_pattern.findall (stringa) # Out: elenco di tutte le sottostringhe corrispondenti
precompiled_pattern.sub (stringa / modello / funzione, stringa) # Out: stringa sostituita
Corrisponde all'inizio di una stringa
Il primo argomento di re.match()
è l'espressione regolare, il secondo è la stringa da abbinare:
import re
pattern = r"123"
string = "123zzb"
re.match(pattern, string)
# Out: <_sre.SRE_Match object; span=(0, 3), match='123'>
match = re.match(pattern, string)
match.group()
# Out: '123'
Si può notare che la variabile pattern è una stringa preceduta da r
, che indica che la stringa è un letterale stringa raw .
Un letterale stringa raw ha una sintassi leggermente diversa rispetto a un letterale stringa, ovvero una barra rovesciata \
in una stringa raw significa letteralmente "solo un backslash" e non è necessario raddoppiare i backlashes per evitare "sequenze di escape" come newlines ( \n
) , schede ( \t
), backspaces ( \
), form-feeds ( \r
) e così via. In normali stringhe di stringa, ogni backslash deve essere raddoppiata per evitare di essere presa come l'inizio di una sequenza di escape.
Quindi, r"\n"
è una stringa di 2 caratteri: \
e n
. Anche i pattern Regex usano i backslash, ad esempio \d
riferisce a qualsiasi carattere numerico. È possibile evitare di dover eseguire il doppio escape delle stringhe ( "\\d"
) utilizzando stringhe non elaborate ( r"\d"
).
Per esempio:
string = "\\t123zzb" # here the backslash is escaped, so there's no tab, just '\' and 't'
pattern = "\\t123" # this will match \t (escaping the backslash) followed by 123
re.match(pattern, string).group() # no match
re.match(pattern, "\t123zzb").group() # matches '\t123'
pattern = r"\\t123"
re.match(pattern, string).group() # matches '\\t123'
La corrispondenza viene eseguita solo dall'inizio della stringa. Se vuoi abbinare ovunque usa re.search
:
match = re.match(r"(123)", "a123zzb")
match is None
# Out: True
match = re.search(r"(123)", "a123zzb")
match.group()
# Out: '123'
Ricerca
pattern = r"(your base)"
sentence = "All your base are belong to us."
match = re.search(pattern, sentence)
match.group(1)
# Out: 'your base'
match = re.search(r"(belong.*)", sentence)
match.group(1)
# Out: 'belong to us.'
La ricerca viene eseguita ovunque nella stringa, diversamente da re.match
. Puoi anche usare re.findall
.
Puoi anche cercare all'inizio della stringa (usa ^
),
match = re.search(r"^123", "123zzb")
match.group(0)
# Out: '123'
match = re.search(r"^123", "a123zzb")
match is None
# Out: True
alla fine della stringa (usa $
),
match = re.search(r"123$", "zzb123")
match.group(0)
# Out: '123'
match = re.search(r"123$", "123zzb")
match is None
# Out: True
o entrambi (usa sia ^
che $
):
match = re.search(r"^123$", "123")
match.group(0)
# Out: '123'
Raggruppamento
Il raggruppamento è fatto con parentesi. Calling group()
restituisce una stringa formata dai sottogruppi parentesi corrispondenti.
match.group() # Group without argument returns the entire match found
# Out: '123'
match.group(0) # Specifying 0 gives the same result as specifying no argument
# Out: '123'
Gli argomenti possono anche essere forniti a group()
per recuperare un determinato sottogruppo.
Dai documenti :
Se c'è un singolo argomento, il risultato è una singola stringa; se ci sono più argomenti, il risultato è una tupla con un elemento per argomento.
Calling groups()
d'altra parte, restituisce una lista di tuple contenenti i sottogruppi.
sentence = "This is a phone number 672-123-456-9910"
pattern = r".*(phone).*?([\d-]+)"
match = re.match(pattern, sentence)
match.groups() # The entire match as a list of tuples of the paranthesized subgroups
# Out: ('phone', '672-123-456-9910')
m.group() # The entire match as a string
# Out: 'This is a phone number 672-123-456-9910'
m.group(0) # The entire match as a string
# Out: 'This is a phone number 672-123-456-9910'
m.group(1) # The first parenthesized subgroup.
# Out: 'phone'
m.group(2) # The second parenthesized subgroup.
# Out: '672-123-456-9910'
m.group(1, 2) # Multiple arguments give us a tuple.
# Out: ('phone', '672-123-456-9910')
Gruppi con nome
match = re.search(r'My name is (?P<name>[A-Za-z ]+)', 'My name is John Smith')
match.group('name')
# Out: 'John Smith'
match.group(1)
# Out: 'John Smith'
Crea un gruppo di cattura che può essere referenziato per nome e per indice.
Gruppi non catturanti
Usando (?:)
crea un gruppo, ma il gruppo non viene catturato. Ciò significa che puoi usarlo come gruppo, ma non inquinerà il tuo "spazio di gruppo".
re.match(r'(\d+)(\+(\d+))?', '11+22').groups()
# Out: ('11', '+22', '22')
re.match(r'(\d+)(?:\+(\d+))?', '11+22').groups()
# Out: ('11', '22')
Questo esempio corrisponde a 11+22
o 11
, ma non 11+
. Questo perché il segno +
e il secondo termine sono raggruppati. D'altra parte, il segno +
non viene catturato.
Escaping Special Characters
I caratteri speciali (come le parentesi [
e ]
sotto) della classe di caratteri non sono letteralmente abbinati:
match = re.search(r'[b]', 'a[b]c')
match.group()
# Out: 'b'
Sfuggendo ai caratteri speciali, possono essere abbinati letteralmente:
match = re.search(r'\[b\]', 'a[b]c')
match.group()
# Out: '[b]'
La funzione re.escape()
può essere utilizzata per fare questo per te:
re.escape('a[b]c')
# Out: 'a\\[b\\]c'
match = re.search(re.escape('a[b]c'), 'a[b]c')
match.group()
# Out: 'a[b]c'
La funzione re.escape()
sfugge a tutti i caratteri speciali, quindi è utile se si sta scrivendo un'espressione regolare basata sull'input dell'utente:
username = 'A.C.' # suppose this came from the user
re.findall(r'Hi {}!'.format(username), 'Hi A.C.! Hi ABCD!')
# Out: ['Hi A.C.!', 'Hi ABCD!']
re.findall(r'Hi {}!'.format(re.escape(username)), 'Hi A.C.! Hi ABCD!')
# Out: ['Hi A.C.!']
Sostituzione
Le sostituzioni possono essere fatte su stringhe usando re.sub
.
Sostituire le stringhe
re.sub(r"t[0-9][0-9]", "foo", "my name t13 is t44 what t99 ever t44")
# Out: 'my name foo is foo what foo ever foo'
Utilizzando riferimenti di gruppo
Sostituzioni con un numero limitato di gruppi possono essere effettuate come segue:
re.sub(r"t([0-9])([0-9])", r"t\2\1", "t13 t19 t81 t25")
# Out: 't31 t91 t18 t52'
Tuttavia, se si crea un ID di gruppo come "10", questo non funziona : \10
viene letto come 'ID numero 1 seguito da 0'. Quindi devi essere più specifico e usare la notazione \g<i>
:
re.sub(r"t([0-9])([0-9])", r"t\g<2>\g<1>", "t13 t19 t81 t25")
# Out: 't31 t91 t18 t52'
Utilizzando una funzione di sostituzione
items = ["zero", "one", "two"]
re.sub(r"a\[([0-3])\]", lambda match: items[int(match.group(1))], "Items: a[0], a[1], something, a[2]")
# Out: 'Items: zero, one, something, two'
Trova tutte le corrispondenze non sovrapposte
re.findall(r"[0-9]{2,3}", "some 1 text 12 is 945 here 4445588899")
# Out: ['12', '945', '444', '558', '889']
Notare che il r
prima di "[0-9]{2,3}"
dice a python di interpretare la stringa as-is; come una stringa "grezza".
Puoi anche usare re.finditer()
che funziona allo stesso modo di re.findall()
ma restituisce un iteratore con oggetti SRE_Match
invece di un elenco di stringhe:
results = re.finditer(r"([0-9]{2,3})", "some 1 text 12 is 945 here 4445588899")
print(results)
# Out: <callable-iterator object at 0x105245890>
for result in results:
print(result.group(0))
''' Out:
12
945
444
558
889
'''
Modelli precompilati
import re
precompiled_pattern = re.compile(r"(\d+)")
matches = precompiled_pattern.search("The answer is 41!")
matches.group(1)
# Out: 41
matches = precompiled_pattern.search("Or was it 42?")
matches.group(1)
# Out: 42
La compilazione di un modello consente di riutilizzarlo in seguito in un programma. Tuttavia, si noti che Python memorizza nella cache le espressioni utilizzate di recente ( documenti , risposta SO ), quindi "i programmi che utilizzano solo alcune espressioni regolari alla volta non devono preoccuparsi di compilare le espressioni regolari" .
import re
precompiled_pattern = re.compile(r"(.*\d+)")
matches = precompiled_pattern.match("The answer is 41!")
print(matches.group(1))
# Out: The answer is 41
matches = precompiled_pattern.match("Or was it 42?")
print(matches.group(1))
# Out: Or was it 42
Può essere usato con re.match ().
Verifica dei caratteri consentiti
Se vuoi verificare che una stringa contenga solo un determinato set di caratteri, in questo caso az, AZ e 0-9, puoi farlo in questo modo,
import re
def is_allowed(string):
characherRegex = re.compile(r'[^a-zA-Z0-9.]')
string = characherRegex.search(string)
return not bool(string)
print (is_allowed("abyzABYZ0099"))
# Out: 'True'
print (is_allowed("#*@#$%^"))
# Out: 'False'
È anche possibile adattare la linea di espressione da [^a-zA-Z0-9.]
[^a-z0-9.]
, Per non consentire le lettere maiuscole, ad esempio.
Credito parziale: http://stackoverflow.com/a/1325265/2697955
Dividere una stringa usando le espressioni regolari
Puoi anche usare espressioni regolari per dividere una stringa. Per esempio,
import re
data = re.split(r'\s+', 'James 94 Samantha 417 Scarlett 74')
print( data )
# Output: ['James', '94', 'Samantha', '417', 'Scarlett', '74']
bandiere
Per alcuni casi speciali abbiamo bisogno di cambiare il comportamento dell'Espressione Regolare, questo viene fatto usando le bandiere. Le bandiere possono essere impostate in due modi, attraverso la parola chiave flags
o direttamente nell'espressione.
Parola chiave delle bandiere
Di seguito un esempio per re.search
ma funziona per la maggior parte delle funzioni nel modulo re
.
m = re.search("b", "ABC")
m is None
# Out: True
m = re.search("b", "ABC", flags=re.IGNORECASE)
m.group()
# Out: 'B'
m = re.search("a.b", "A\nBC", flags=re.IGNORECASE)
m is None
# Out: True
m = re.search("a.b", "A\nBC", flags=re.IGNORECASE|re.DOTALL)
m.group()
# Out: 'A\nB'
Bandiere comuni
Bandiera | breve descrizione |
---|---|
re.IGNORECASE , re.I | Fa in modo che il modello ignori il caso |
re.DOTALL , re.S | Rende . abbinare tutto compreso a capo |
re.MULTILINE , re.M | Rende ^ corrisponde all'inizio di una linea e $ alla fine di una linea |
re.DEBUG | Attiva le informazioni di debug |
Per l'elenco completo di tutte le flag disponibili, controlla i documenti
Bandiere in linea
Dai documenti :
(?iLmsux)
(Una o più lettere dal set 'i', 'L', 'm', 's', 'u', 'x'.)Il gruppo corrisponde alla stringa vuota; le lettere impostano i flag corrispondenti: re.I (ignora il caso), re.L (dipendente dalla locale), re.M (multi-line), re.S (punto corrisponde a tutti), re.U (dipendente da Unicode), e re.X (dettagliato), per l'intera espressione regolare. Ciò è utile se si desidera includere i flag come parte dell'espressione regolare, invece di passare un argomento flag alla funzione re.compile ().
Si noti che il flag (? X) cambia il modo in cui l'espressione viene analizzata. Dovrebbe essere usato prima nella stringa di espressione o dopo uno o più caratteri di spaziatura. Se ci sono caratteri non spazi bianchi prima del flag, i risultati non sono definiti.
Iterare le partite usando `re.finditer`
È possibile utilizzare re.finditer
per re.finditer
tutte le corrispondenze in una stringa. Questo ti dà (in confronto a re.findall
informazioni extra, come ad esempio informazioni sulla posizione della partita nella stringa (indici):
import re
text = 'You can try to find an ant in this string'
pattern = 'an?\w' # find 'an' either with or without a following word character
for match in re.finditer(pattern, text):
# Start index of match (integer)
sStart = match.start()
# Final index of match (integer)
sEnd = match.end()
# Complete match (string)
sGroup = match.group()
# Print match
print('Match "{}" found at: [{},{}]'.format(sGroup, sStart,sEnd))
Risultato:
Match "an" found at: [5,7]
Match "an" found at: [20,22]
Match "ant" found at: [23,26]
Abbina un'espressione solo in posizioni specifiche
Spesso vuoi abbinare un'espressione solo in luoghi specifici (lasciandoli intatti in altri, cioè). Considera la seguente frase:
An apple a day keeps the doctor away (I eat an apple everyday).
Qui la "mela" si verifica due volte che può essere risolta con i cosiddetti verbi di controllo backtracking che sono supportati dal modulo regex
più recente. L'idea è:
forget_this | or this | and this as well | (but keep this)
Con il nostro esempio di mela, questo sarebbe:
import regex as re
string = "An apple a day keeps the doctor away (I eat an apple everyday)."
rx = re.compile(r'''
\([^()]*\) (*SKIP)(*FAIL) # match anything in parentheses and "throw it away"
| # or
apple # match an apple
''', re.VERBOSE)
apples = rx.findall(string)
print(apples)
# only one
Questo corrisponde a "mela" solo quando può essere trovato al di fuori delle parentesi.
Ecco come funziona:
- Mentre guarda da sinistra a destra , il motore regex consuma tutto a sinistra, il
(*SKIP)
agisce come una "affermazione sempre vera". Successivamente, fallisce correttamente(*FAIL)
e backtrack. - Ora arriva al punto di
(*SKIP)
da destra a sinistra (aka mentre backtracking) dove è vietato andare ulteriormente a sinistra. Invece, al motore viene detto di gettare via qualsiasi cosa a sinistra e saltare al punto in cui è stato invocato il(*SKIP)
.