W3docs

Gruppi di stringhe Python — Gruppi di cattura Regex

Impara i gruppi di cattura regex in Python: group(), groups(), gruppi nominati, non-cattura e backreference con esempi chiari.

Quando il modulo re di Python trova una corrispondenza all'interno di una stringa, restituisce un oggetto match. L'oggetto match è più di un semplice risultato sì/no — ricorda ogni sotto-pattern che hai racchiuso tra parentesi, chiamato gruppo di cattura. I metodi .group() e .groups() sono il modo in cui si recuperano quei frammenti catturati.

Questo capitolo tratta tutto ciò che è necessario per usare i gruppi con sicurezza: gruppi numerati, gruppi nominati, gruppi non-catturanti, backreference nelle sostituzioni e trappole comuni.

Cos'è un Gruppo di Cattura?

Un gruppo di cattura è una parte di un pattern regex racchiusa tra parentesi semplici (...). Quando il pattern trova una corrispondenza, Python salva separatamente il testo abbinato da ogni coppia di parentesi, oltre a salvare l'intera corrispondenza.

import re

m = re.search(r'(\d{4})-(\d{2})-(\d{2})', '2024-03-15')

print(m.group(0))  # 2024-03-15  — the whole match
print(m.group(1))  # 2024        — first group
print(m.group(2))  # 03          — second group
print(m.group(3))  # 15          — third group

I gruppi sono numerati da sinistra a destra, a partire da 1, nell'ordine in cui appare la parentesi aperta. group(0) (o semplicemente group() senza argomenti) restituisce sempre l'intera corrispondenza.

Il Metodo .group()

.group(n) restituisce il testo abbinato dall'n-esimo gruppo di cattura. Puoi passare più indici contemporaneamente per ottenere una tupla di risultati.

import re

m = re.search(r'(\w+)@(\w+)\.(\w+)', '[email protected]')

# Individual groups
print(m.group(1))       # alice
print(m.group(2))       # example
print(m.group(3))       # com

# Multiple at once
print(m.group(1, 3))    # ('alice', 'com')

Se un gruppo esiste nel pattern ma non ha partecipato alla corrispondenza (ad esempio, era all'interno di una sezione opzionale), group(n) restituisce None.

import re

# group(1) is optional — it may not match
m = re.search(r'(\d+)?-(\d+)', 'abc-456')
print(m.group(1))  # None  (the optional \d+ did not match)
print(m.group(2))  # 456

Il Metodo .groups()

.groups() restituisce una tupla contenente il testo abbinato da ogni gruppo di cattura, in ordine. È una comoda scorciatoia quando si vogliono tutti i gruppi in una volta sola.

import re

m = re.search(r'(\d{4})-(\d{2})-(\d{2})', '2024-03-15')
print(m.groups())  # ('2024', '03', '15')

Fornire un Valore Predefinito per i Gruppi Non Abbinati

Se un gruppo non ha partecipato alla corrispondenza, appare come None nella tupla. Puoi fornire un argomento default per sostituire quei valori None con qualcosa di più utile.

import re

m = re.search(r'(\d+)?-(\d+)', 'abc-456')
print(m.groups())              # (None, '456')
print(m.groups(default='0'))   # ('0', '456')

Gruppi Nominati con (?P<name>...)

I gruppi numerati diventano difficili da tracciare in pattern complessi. I gruppi nominati permettono di assegnare un'etichetta significativa a ciascun gruppo usando la sintassi (?P<name>...), per poi recuperare il valore per nome anziché per posizione.

import re

pattern = r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'
m = re.search(pattern, '2024-03-15')

print(m.group('year'))   # 2024
print(m.group('month'))  # 03
print(m.group('day'))    # 15

I gruppi nominati hanno comunque un numero, quindi puoi accedervi in entrambi i modi.

Il Metodo .groupdict()

Quando si hanno gruppi nominati, .groupdict() restituisce un dizionario che mappa ogni nome al testo abbinato. Questo è utile quando si vuole estrarre una corrispondenza in un oggetto strutturato.

import re

m = re.search(
    r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})',
    '2024-03-15'
)
print(m.groupdict())
# {'year': '2024', 'month': '03', 'day': '15'}

Puoi poi usare il risultato direttamente:

date_parts = m.groupdict()
print(f"{date_parts['day']}/{date_parts['month']}/{date_parts['year']}")
# 15/03/2024

Gruppi Non-Catturanti con (?:...)

A volte è necessario raggruppare una parte di un pattern allo scopo di applicare un quantificatore o un'alternanza, ma non si vuole che quel gruppo appaia nei risultati della corrispondenza. Si usa (?:...) — un gruppo non-catturante.

import re

# Without non-capturing group: both parts appear in groups()
m1 = re.search(r'(\d{4})-(\d{2})-(\d{2})', '2024-03-15')
print(m1.groups())  # ('2024', '03', '15')

# Year grouped but not captured — month and day are groups 1 and 2
m2 = re.search(r'(?:\d{4})-(\d{2})-(\d{2})', '2024-03-15')
print(m2.groups())  # ('03', '15')

I gruppi non-catturanti sono una buona pratica quando non si ha bisogno di una particolare sotto-corrispondenza — mantengono gli indici dei gruppi ordinati ed evitano sprechi di memoria.

Gruppi Annidati

I gruppi possono essere annidati. La numerazione è sempre basata sulla posizione della parentesi aperta, contando da sinistra a destra.

import re

# ((\d{4})-(\d{2}))-(\d{2})
# group 1 = the outer (year-month pair)
# group 2 = year
# group 3 = month
# group 4 = day
m = re.search(r'((\d{4})-(\d{2}))-(\d{2})', '2024-03-15')
print(m.group(1))   # 2024-03
print(m.group(2))   # 2024
print(m.group(3))   # 03
print(m.group(4))   # 15
print(m.groups())   # ('2024-03', '2024', '03', '15')

Gruppi con re.findall()

Quando si usa re.findall() e il pattern contiene esattamente un gruppo di cattura, restituisce una lista di stringhe — una per corrispondenza.

Quando il pattern contiene due o più gruppi di cattura, restituisce una lista di tuple.

import re

# One group → list of strings
emails = '[email protected], [email protected]'
usernames = re.findall(r'(\w+)@\w+\.\w+', emails)
print(usernames)  # ['alice', 'bob']

# Two groups → list of tuples
pairs = re.findall(r'(\w+)@(\w+)\.com', emails)
print(pairs)  # [('alice', 'gmail'), ('bob', 'yahoo')]

Gruppi con re.finditer()

re.finditer() restituisce un iteratore di oggetti match, quindi puoi accedere a .group() e .groups() su ciascuno individualmente. Questo è l'approccio più flessibile quando hai bisogno dei dettagli completi della corrispondenza per ogni occorrenza.

import re

text = '[email protected], [email protected]'
for m in re.finditer(r'(?P<user>\w+)@(?P<domain>\w+)\.com', text):
    print(m.group('user'), '->', m.group('domain'))
# alice -> gmail
# bob   -> yahoo

Backreference in re.sub()

I gruppi di cattura alimentano anche le backreference in re.sub(). Nella stringa di sostituzione, \1, \2, … si riferiscono al testo catturato dal gruppo 1, gruppo 2, e così via. I gruppi nominati usano \g<name>.

import re

# Swap first and last name
result = re.sub(r'(\w+)\s(\w+)', r'\2 \1', 'John Smith')
print(result)  # Smith John

# Same using named groups
result2 = re.sub(
    r'(?P<first>\w+)\s(?P<last>\w+)',
    r'\g<last> \g<first>',
    'Jane Doe'
)
print(result2)  # Doe Jane

Posizione di un Gruppo: .start(), .end(), .span()

Gli oggetti match espongono la posizione di ogni gruppo, non solo il suo testo. Passa un numero di gruppo (o un nome) a .start(), .end(), o .span().

import re

m = re.search(r'(\d{4})-(\d{2})', '2024-03')
print(m.span(1))    # (0, 4)  — year occupies indices 0..3
print(m.start(2))   # 5       — month starts at index 5
print(m.end(2))     # 7       — month ends before index 7

Riferimento Rapido

MetodoRestituisceNote
m.group() o m.group(0)Intera stringa corrispondenteSempre disponibile
m.group(n)Testo abbinato dal gruppo nNone se il gruppo non ha abbinato
m.group(n, m, ...)Tupla di gruppiPiù indici in una sola chiamata
m.groups()Tupla di tutti i gruppidefault= opzionale per i non abbinati
m.groupdict()Dizionario dei gruppi nominatiAppaiono solo i gruppi nominati
m.start(n) / m.end(n)Indice di inizio / fine del gruppo nPassa 0 per l'intera corrispondenza
m.span(n)Tupla (start, end)Abbreviazione per entrambi

Errori Comuni

Usare .group() senza verificare None. re.search() restituisce None quando non c'è corrispondenza, quindi chiamare .group() direttamente sul risultato genera AttributeError. Proteggi sempre la chiamata:

import re

m = re.search(r'(\d+)', 'no digits here')
if m:
    print(m.group(1))
else:
    print('no match')
# no digits here

Confondere .group() e .groups(). .group(1) restituisce una string; .groups() restituisce sempre una tupla. Mescolarli causa sottili errori di tipo.

Dimenticare che re.findall() cambia forma con i gruppi. Senza gruppi restituisce una lista piatta di stringhe; con i gruppi restituisce una lista di tuple. Aggiungere o rimuovere un gruppo di cattura può rompere silenziosamente il codice che elabora il risultato.

Quando Usare Ogni Approccio

  • Usa i gruppi numerati per pattern semplici e brevi con pochi gruppi.
  • Usa i gruppi nominati quando i pattern sono complessi, o quando utilizzerai il risultato su più righe di codice — i nomi fungono da auto-documentazione.
  • Usa i gruppi non-catturanti (?:...) ogni volta che hai bisogno di raggruppare solo per applicare un quantificatore o un'alternanza, non per estrarre un valore.
  • Usa .groups() per estrarre tutte le catture in una volta sola in una tupla.
  • Usa .groupdict() quando tutti i gruppi sono nominati e vuoi un dizionario.

Per una panoramica più ampia del modulo re e della sua sintassi dei pattern, consulta il capitolo Python RegEx. Per le operazioni sulle stringhe che non richiedono regex, consulta Modify Strings e Python String Methods.

Esercitati

Pratica
In Python, which functions or methods can be used to define a group within strings?
In Python, which functions or methods can be used to define a group within strings?
Was this page helpful?