2011-11-26 11 views
6

Załóżmy, że mam jakieś StringIO (od cStringIO). Chcę odczytać bufor z nim aż do niektórych znaków/bajt spotyka, mówią „Z”, więc:szybki sposób na odczyt z StringIO, aż napotka jakiś bajt.

stringio = StringIO('ABCZ123') 
buf = read_until(stringio, 'Z') # buf is now 'ABCZ' 
# strinio.tell() is now 4, pointing after 'Z' 

Jaki jest najszybszy sposób, aby zrobić to w Pythonie? Dziękuję

Odpowiedz

4

ja bardzo rozczarowany, że to pytanie dostać tylko jedną odpowiedź na przepełnienie stosu, ponieważ jest to ciekawe i istotne pytanie. W każdym razie, ponieważ tylko ovgolovin dać rozwiązanie i ja thinked to może powoli, myślałem szybsze rozwiązanie:

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) 

to przeczytać IO w kawałkach (może zakończyć char nie znaleziono w pierwszym fragmencie). Jest bardzo szybki, ponieważ dla każdego znaku nie jest wywoływana żadna funkcja Pythona, ale przeciwnie, maksymalne użycie napisanych przez C funkcji Pythona.

Ten przebieg około 60x szybciej niż rozwiązanie ovgolovin. Uruchomiłem timeit, aby to sprawdzić.

+0

Bardzo dobre rozwiązanie! Rozwiązuje problem nadmiernego obciążania Pythona wywołaniami funkcji. Jedynym minusem jest to, że przechowujesz w pamięci nadmiarowy obiekt 'datalist'. Możliwe jest przepisanie tego kodu za pomocą generatora zamiast funkcji ('join' akceptuje iteratory), więc w pamięci nie będzie żadnych tymczasowych nadmiarowych obiektów. – ovgolovin

+0

Ale wersja generatora okazuje się nieco wolniejsza: http://ideone.com/dQGe5 (Jeśli łańcuch jest duży (1 milion symboli) - wtedy wersja generatora jest nieco szybsza). – ovgolovin

+0

Nawiasem mówiąc, dlaczego wybrałeś fragmenty symboli "256"? (dlaczego nie '512' lub' 1024'?) – ovgolovin

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

Tutaj iter jest używany w tym trybie: iter(callable, sentinel) -> iterator.

''.join(...) jest dość skuteczny. Ostatnia operacja dodawania "Z" ''.join(i) + 'Z' nie jest dobra. Ale może być skierowana przez dodanie 'Z' do iteracyjnej:

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) 

jeden sposób, aby to zrobić jest użycie generatora:

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) 

Zrobiłem kilka testów wydajności. Realizacja opisanych technik jest całkiem takie same:

http://ideone.com/dQGe5

+0

, ale "Z" nie jest wtedy pobierane ze strumienia, czy jest? – zaharpopov

+0

@zaharpopov Nie, jest upuszczony. Użyłem więc '+ 'Z'' i' chain (i, repeat (' Z ', 1)), aby rozwiązać ten problem. Wiemy, co używamy jako wartownika, więc możemy łatwo dodać go do strumienia ręcznie. – ovgolovin

+0

Спасибо za wysiłek, ale zobacz moją odpowiedź – zaharpopov

Powiązane problemy