2011-11-26 6 views

risposta

4

ho molto deluso che la questione ottenere una sola risposta in caso di overflow dello stack, perché è domanda interessante e pertinente. In ogni caso, dal momento che solo ovgolovin dare soluzione ed ho pensata forse è lento, ho pensato che una soluzione più veloce:

def foo(stringio): 
    datalist = [] 
    while True: 
     chunk = stringio.read(256) 
     i = chunk.find('Z') 
     if i == -1: 
      datalist.append(chunk) 
     else: 
      datalist.append(chunk[:i+1]) 
      break 
     if len(chunk) < 256: 
      break 
    return ''.join(datalist) 

Questo io letta in blocchi (forse finire char non trovato nel primo blocco). È molto veloce perché nessuna funzione Python ha chiamato per ogni carattere, ma al contrario uso massimo delle funzioni Python scritte in C.

Questa corsa circa 60 volte più veloce della soluzione di ovgolovin. Ho eseguito timeit per controllarlo.

+0

Ottima soluzione! Affronta il pesante sovraccarico di Python sulle chiamate di funzione. L'unico svantaggio è che si tiene in memoria un oggetto 'datalist' ridondante. È possibile riscrivere questo codice con il generatore anziché con la funzione ('join' accetta gli iteratori), quindi non ci saranno oggetti temporanei ridondanti in memoria. – ovgolovin

+0

Ma la versione del generatore risulta un po 'più lenta: http://ideone.com/dQGe5 (Se una stringa è grande (1 mln di simboli), la versione del generatore è un po' più veloce). – ovgolovin

+0

A proposito, perché hai scelto blocchi di simboli "256"? (perché non '512' o' 1024'?) – ovgolovin

2
i = iter(lambda: stringio.read(1),'Z') 
buf = ''.join(i) + 'Z' 

Qui iter è utilizzato in questo modo: iter(callable, sentinel) -> iterator.

''.join(...) è abbastanza efficace. L'ultima operazione di aggiunta di "Z" ''.join(i) + 'Z' non è buona. Ma può essere affrontato con l'aggiunta 'Z' al iteratore:

from itertools import chain, repeat 

stringio = StringIO.StringIO('ABCZ123') 
i = iter(lambda: stringio.read(1),'Z') 
i = chain(i,repeat('Z',1)) 
buf = ''.join(i) 

Un altro modo per farlo è quello di utilizzare generatore:

def take_until_included(stringio): 
    while True: 
     s = stringio.read(1) 
     yield s 
     if s=='Z': 
      return 

i = take_until_included(stringio) 
buf = ''.join(i) 

ho fatto alcuni test di efficienza. Le prestazioni delle tecniche descritte è piuttosto la stessa:

http://ideone.com/dQGe5

+0

ma "Z" non è preso dallo stream, o è vero? – zaharpopov

+0

@zaharpopov No, è caduto. Quindi ho usato '+ 'Z'' e' chain (i, repeat (' Z ', 1)) 'per risolvere questo problema. Sappiamo cosa usiamo come sentinella, quindi possiamo facilmente aggiungerlo allo stream manualmente. – ovgolovin

+0

Спасибо per il tuo impegno, ma vedi la mia risposta – zaharpopov