2011-11-13 16 views
13

Devo trovare tutte le corrispondenze in una stringa per una determinata regex. Sto usando findall() per farlo finché non mi sono imbattuto in un caso in cui non stava facendo quello che mi aspettavo. Per esempio:python - regex search and findall

regex = re.compile('(\d+,?)+') 
s = 'There are 9,000,000 bicycles in Beijing.' 

print re.search(regex, s).group(0) 
> 9,000,000 

print re.findall(regex, s) 
> ['000'] 

In questo caso search() restituisce quello che mi serve (la corrispondenza più lunga), ma findall() comporta in modo diverso, anche se la documentazione implica che dovrebbe essere lo stesso:

findall() partite tutte le occorrenze di un modello, non solo il primo come search() fa.

  • Perché il comportamento diverso?

  • Come è possibile ottenere il risultato di search() con findall() (o altro)?

+0

try ''([\ d,] +)'' – dawg

risposta

15

Ok, vedo quello che sta succedendo ... dalla documentazione:

Se uno o più gruppi sono presenti nel modello, restituire una lista di gruppi; questo sarà un elenco di tuple se il modello ha più di un gruppo.

Come si è visto, si dispone di un gruppo, "(\ d + ,?)" ... sì, qual è il ritorno è l'ultima occorrenza di questo gruppo, o 000.

Una soluzione è quello di circondare l'intera regex da un gruppo, come questo

regex = re.compile('((\d+,?)+)') 

allora, esso ritorna [('9.000.000', '000')], che è una tupla contenente entrambi i gruppi appaiati. certo, ti interessa solo il primo.

Personalmente, vorrei utilizzare la seguente espressione regolare

regex = re.compile('((\d+,)*\d+)') 

per evitare corrispondenza cose come "questo è un cattivo numero di 9.123,"

Modifica.

Ecco un modo per evitare di dover circondare l'espressione da parentesi o trattare con tuple

s = "..." 
regex = re.compile('(\d+,?)+') 
it = re.finditer(regex, s) 

for match in it: 
    print match.group(0) 

finditer restituisce un iteratore che è possibile utilizzare per accedere a tutte le corrispondenze trovate. questi oggetti match sono gli stessi che re.search restituisce, quindi group (0) restituisce il risultato che ti aspetti.

+0

Grazie per la spiegazione. Si scopre che 'finditer' era in realtà più adatto a quello che stavo facendo come suggerivi tu. La regex viene dall'input dell'utente, quindi non ho il controllo su di esso. – armandino

7

@ aleph_null's answer correttamente spiega che cosa sta causando il tuo problema, ma penso di avere una soluzione migliore.Utilizzare questa espressione regolare:

regex = re.compile(r'\d+(?:,\d+)*') 

Alcuni motivi per cui è meglio:

  1. (?:...) è un gruppo non-cattura, in modo da ottenere solo un risultato per ogni partita.

  2. \d+(?:,\d+)* è una regex migliore, più efficiente e meno probabile che restituisca i falsi positivi.

  3. È consigliabile utilizzare sempre le stringhe raw di Python per le regex, se possibile; è meno probabile che tu sia sorpreso dalle sequenze di escape di espressioni regolari (come \b per word boundary) interpretate come sequenze di escape stringa-letterali (come \b per backspace).

+0

Grazie Alan! Avrei dovuto accennare prima ma non ho il controllo sulla regex come input dell'utente .. – armandino

+2

Nessun problema! Ma, per la cronaca, lasciare che gli utenti inseriscano le espressioni regolari da eseguire dalla tua app è una cattiva idea. Quando le regex scritte male (o semplicemente digitate in fretta) non riescono a corrispondere, o mandano in crash il sistema, incolperanno * te * per questo. ;) –