быстрый способ чтения из StringIO, пока не встретится некоторый байт - PullRequest
6 голосов
/ 26 ноября 2011

Предположим, у меня есть StringIO (из cStringIO). Я хочу читать из него буфер, пока не встретится какой-нибудь символ / байт, скажем 'Z', поэтому:

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

Какой самый быстрый способ сделать это в Python? Спасибо

Ответы [ 2 ]

5 голосов
/ 27 ноября 2011

Я очень разочарован тем, что на этот вопрос получают только один ответ на переполнение стека, потому что это интересный и актуальный вопрос. Во всяком случае, так как только oggolovin дают решение, и я подумал, что это может быть медленно, я подумал, более быстрое решение:

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)

Это читается в виде фрагментов (возможно, конец символа найден не в первом фрагменте). Это очень быстро, потому что ни одна функция Python не вызывается для каждого символа, а наоборот - максимальное использование написанных на C функций Python.

Этот прогон примерно в 60 раз быстрее, чем раствор Овголовина. Я пробежал timeit, чтобы проверить его.

2 голосов
/ 26 ноября 2011
i = iter(lambda: stringio.read(1),'Z')
buf = ''.join(i) + 'Z'

Здесь iter используется в этом режиме: iter(callable, sentinel) -> iterator.

''.join(...) довольно эффективно.Последняя операция добавления 'Z' ''.join(i) + 'Z' не так хороша.Но это можно решить, добавив 'Z' к итератору:

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)

Еще один способ сделать это - использовать генератор:

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)

Я сделал некоторую эффективностьтесты.Производительность описанных методик практически одинакова:

http://ideone.com/dQGe5

...