2011-02-04 10 views
12

Sto cercando il modo più veloce per sostituire un numero elevato di sottostringhe all'interno di una stringa molto grande. Ecco due esempi che ho usato.Il metodo Python più veloce per la ricerca e la sostituzione su una stringa di grandi dimensioni

findall() è più semplice ed elegante, ma richiede una quantità incredibile di tempo.

finditer() passa attraverso un file di grandi dimensioni, ma non sono sicuro che questo sia il modo giusto per farlo.

Ecco alcuni esempi di codice. Tieni presente che il testo effettivo a cui sono interessato è una singola stringa di circa 10 MB di dimensioni e c'è un'enorme differenza in questi due metodi.

import re 

def findall_replace(text, reg, rep): 
    for match in reg.findall(text): 
     output = text.replace(match, rep) 
    return output 

def finditer_replace(text, reg, rep): 
    cursor_pos = 0 
    output = '' 
    for match in reg.finditer(text): 
     output += "".join([text[cursor_pos:match.start(1)], rep]) 
     cursor_pos = match.end(1) 
    output += "".join([text[cursor_pos:]]) 
    return output 

reg = re.compile(r'(dog)') 
rep = 'cat' 
text = 'dog cat dog cat dog cat' 

finditer_replace(text, reg, rep) 

findall_replace(text, reg, rep) 

UPDATE Aggiunto metodo re.sub a test:

def sub_replace(reg, rep, text): 
    output = re.sub(reg, rep, text) 
    return output 

Risultati

re.sub() - 0: 00: 00.031000
finditer() - 0 : 00: 00.109000
findall() - 0: 01: 17.260000

+0

e il secondo è davvero molto più veloce? Sembra strano per me, dovrebbero prendere ca. lo stesso tempo. E penso che entrambi i modi siano corretti. –

+0

perché non stai usando il metodo sub di re? –

+1

L'uso di + = con le stringhe è un'operazione O (n^2), rispetto alla O (n) della costruzione di un elenco e l'utilizzo di "" per unirsi. –

risposta

14

Il metodo standard è quello di utilizzare il built-in

re.sub(reg, rep, text) 

Tra l'altro la ragione per la differenza di prestazioni tra le versioni è che ogni sostituzione nella prima versione fa sì che l'intera stringa deve essere ricopiato. Le copie sono veloci, ma quando copi 10 MB alla volta, le copie saranno lente.

+0

Grazie. Non ho usato re.sub() perché pensavo che funzionasse nello stesso modo in cui cercavo. Ho eseguito di nuovo i miei test e re.sub è chiaramente il metodo più veloce. I risultati sono stati aggiunti alla domanda. – cyrus

4

È possibile, e penso che è necessario perché sicuramente è una funzione ottimizzata, utilizzare

re.sub(pattern, repl, string[, count, flags]) 

Il motivo per cui il tuo findall_replace() funzione è lunga è che ad ogni partita, un nuovo oggetto stringa è stato creato, come si vedrà dal eseguito il seguente codice:

ch = '''qskfg qmohb561687ipuygvnjoihi2576871987uuiazpoieiohoihnoipoioh 
opuihbavarfgvipauhbi277auhpuitchpanbiuhbvtaoi541987ujptoihbepoihvpoezi 
abtvar473727tta aat tvatbvatzeouithvbop772iezubiuvpzhbepuv454524522ueh''' 

import re 

def findall_replace(text, reg, rep): 
    for match in reg.findall(text): 
     text = text.replace(match, rep) 
     print id(text) 
    return text 

pat = re.compile('\d+') 
rep = 'AAAAAAA' 

print id(ch) 
print 
print findall_replace(ch, pat, rep) 

si noti che in questo codice ho sostituito con output = text.replace(match, rep)text = text.replace(match, rep), altrimenti solo l'ultimo avvenimento viene sostituito.

finditer_replace() è lo stesso motivo per findall_replace(): creazione ripetuta di un oggetto stringa. Ma il primo usa un iteratore re.finditer() mentre quest'ultimo costruisce prima un oggetto lista, quindi è più lungo. Questa è la differenza tra iteratore e non iteratore.

1

Tra l'altro, il codice con findall_replace() non è sicuro, può restituire i risultati unawaited:

ch = 'sea sun ABC-ABC-DEF bling ranch micABC-DEF fish' 

import re 

def findall_replace(text, reg, rep): 
    for gr in reg.findall(text): 
     text = text.replace(gr, rep) 
     print 'group==',gr 
     print 'text==',text 
    return '\nresult is : '+text 

pat = re.compile('ABC-DE') 
rep = 'DEFINITION' 

print 'ch==',ch 
print 
print findall_replace(ch, pat, rep) 

visualizzazione

ch== sea sun ABC-ABC-DEF bling ranch micABC-DEF fish 

group== ABC-DE 
text== sea sun ABC-DEFINITIONF bling ranch micDEFINITIONF fish 
group== ABC-DE 
text== sea sun DEFINITIONFINITIONF bling ranch micDEFINITIONF fish 

result is : sea sun DEFINITIONFINITIONF bling ranch micDEFINITIONF fish 
Problemi correlati