2011-11-26 23 views
6

Supongamos que tengo un StringIO (desde cStringIO). Quiero leer el búfer de ella hasta que algunos caracteres/byte se encuentra, por ejemplo 'Z', por lo que:forma rápida de leer desde StringIO hasta que se encuentre un byte

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

Cuál es la forma más rápida de hacerlo en Python? Gracias

Respuesta

4

que muy decepcionado de que esta pregunta sólo tienen una respuesta en desbordamiento de pila, porque es cuestión interesante y relevante. De todos modos, ya que sólo ovgolovin dar solución y thinked es tal vez lenta, pensé que una solución más rápida:

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) 

Este io leer en trozos (tal vez terminar carbón que no se encuentra en el primer fragmento). Es muy rápido porque ninguna función de Python requería cada carácter, sino el uso máximo de las funciones de Python escritas en C.

Esto funciona aproximadamente 60 veces más rápido que la solución de ovgolovin. Ejecuté timeit para verificarlo.

+0

¡Muy buena solución! Se ocupa de la pesada sobrecarga de Python en llamadas a funciones. El único inconveniente es que tienes en la memoria un objeto 'datalist' redundante. Es posible reescribir este código con generador en lugar de función ('join' acepta iteradores), por lo que no habrá objetos redundantes temporales en la memoria. – ovgolovin

+0

Pero la versión del generador resulta ser un poco más lenta: http://ideone.com/dQGe5 (Si una cadena es grande (símbolos de 1 mln), entonces la versión del generador es un poco más rápida). – ovgolovin

+0

Por cierto, ¿por qué has elegido los trozos de símbolo '256'? (¿por qué no '512' o' 1024'?) – ovgolovin

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

Aquí iter se utiliza en este modo: iter(callable, sentinel) -> iterator.

''.join(...) es bastante eficaz. La última operación de agregar 'Z' ''.join(i) + 'Z' no es tan buena. Pero puede ser abordado mediante la adición de 'Z' al iterador:

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) 

una forma más de hacerlo es utilizar el generador:

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) 

hice algunas pruebas de eficiencia. El rendimiento de las técnicas descritas es bastante la misma:

http://ideone.com/dQGe5

+0

pero 'Z' no se toma de la secuencia, ¿o sí? – zaharpopov

+0

@zaharpopov No, se descarta. Entonces usé '+ 'Z'' y' chain (i, repeat (' Z ', 1)) 'para resolver este problema. Sabemos lo que usamos como centinela, por lo que podemos agregarlo fácilmente a la transmisión de forma manual. – ovgolovin

+0

Спасибо por su esfuerzo, pero vea mi respuesta – zaharpopov

Cuestiones relacionadas