datos rápidos se mueven de un archivo a algún StringIO

En Python tengo una secuencia de archivos y quiero copiar parte de ella en un StringIO . Quiero que esto sea lo más rápido posible, con copia mínima.

Pero si lo hago:

 data = file.read(SIZE) stream = StringIO(data) 

Creo que se hicieron 2 copias, ¿no? Una copia en datos del archivo, otra copia dentro de StringIO en un búfer interno. ¿Puedo evitar una de las copias? No necesito data temporales, así que creo que una copia debería ser suficiente

En resumen: no puedes evitar 2 copias utilizando StringIO.

Algunas suposiciones:

  • Estás usando cStringIO, de lo contrario sería tonto optimizar esto.
  • Lo que buscas es velocidad y no eficiencia de memoria. Si no, vea la solución de Jakob Bowyer, o use una variante utilizando file.read(SOME_BYTE_COUNT) si su archivo es binario.
  • Ya lo ha indicado en los comentarios, pero para completar: desea editar los contenidos, no solo verlos.

Respuesta larga : como las cadenas de python son inmutables y el búfer StringIO no lo es, una copia tendrá que hacerse tarde o temprano; De lo contrario estarías alterando un objeto inmutable! Para lo que quiere que sea posible, el objeto StringIO debería tener un método dedicado que se lea directamente de un objeto de archivo dado como un argumento. No hay tal método.

Fuera de StringIO, hay soluciones que evitan la copia adicional. En la parte superior de mi cabeza, esto leerá un archivo directamente en una matriz de bytes modificable, sin copia adicional:

 import numpy as np a = np.fromfile("filename.ext", dtype="uint8") 

Puede ser complicado trabajar con él, dependiendo del uso que pretenda, ya que es una matriz de valores de 0 a 255, no una matriz de caracteres. Pero es funcionalmente equivalente a un objeto StringIO, y el uso de la np.fromstring , np.tostring , np.tofile y slicing debería llevarte a donde quieras. También es posible que necesite np.insert , np.delete y np.append .

Estoy seguro de que hay otros módulos que harán cosas similares.

CRONOMÉTRALO:

¿Cuánto importa todo esto realmente? Bien, veamos. He hecho un archivo de 100MB, largefile.bin . Luego leo el archivo usando ambos métodos y cambio el primer byte.

 $ python -m timeit -s "importar número como np" "a = np.fromfile ('largefile.bin', 'uint8'); a [0] = 1"
 10 bucles, lo mejor de 3: 132 ms por bucle
 $ python -m timeit -s "de cStringIO import StringIO" "a = StringIO (); a.write (open ('largefile.bin'). read ()); a.seek (0); a.write (' 1 ') "
 10 bucles, lo mejor de 3: 203 ms por bucle

Entonces, en mi caso, usar StringIO es un 50% más lento que usar numpy.

Por último, a modo de comparación, editando el archivo directamente:

 $ python -m timeit "a = abierto ('largefile.bin', 'r + b'); a.seek (0); a.write ('1')"
 10000 bucles, lo mejor de 3: 29.5 usec por bucle

Entonces, es casi 4500 veces más rápido. Por supuesto, es extremadamente dependiente de lo que vas a hacer con el archivo. Alterar el primer byte no es representativo. Pero utilizando este método, tiene una ventaja sobre los otros dos, y como la mayoría de los sistemas operativos tienen un buen almacenamiento de discos, la velocidad también puede ser muy buena.

(Si no tiene permiso para editar el archivo y, por lo tanto, quiere evitar el costo de hacer una copia de trabajo, hay varias formas posibles de boost la velocidad. Si puede elegir el sistema de archivos, Btrfs tiene una copia en Operación de copia de archivo de escritura : hace que el acto de tomar una copia de un archivo sea prácticamente instantáneo. El mismo efecto se puede lograr utilizando una instantánea LVM de cualquier sistema de archivos

No, no hay una copia extra hecha. El búfer utilizado para almacenar los datos es el mismo. Tanto los data como el atributo interno accesible mediante StringIO.getvalue() son nombres diferentes para los mismos datos.

 Python 2.7 (r27:82500, Jul 30 2010, 07:39:35) [GCC 4.1.2 20080704 (Red Hat 4.1.2-48)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import StringIO >>> data = open("/dev/zero").read(1024) >>> hex(id(data)) '0xea516f0' >>> stream = StringIO.StringIO(data) >>> hex(id(stream.getvalue())) '0xea516f0' 

Un cStringIO rápido a la fuente muestra que cStringIO tampoco hace una copia en la construcción, pero sí hace una copia al llamar a cStringIO.getvalue() , por lo que no puedo repetir la demostración anterior.

Tal vez lo que estás buscando es un buffer / memoryview :

 >>> data = file.read(SIZE) >>> buf = buffer(data, 0, len(data)) 

De esta manera puede acceder a una porción de los datos originales sin copiarlos. Sin embargo, debe estar interesado en acceder a esos datos solo en formato orientado a bytes, ya que eso es lo que proporciona el protocolo de búfer.

Puede encontrar más información en esta pregunta relacionada.

Edit: En esta publicación de blog que encontré a través de reddit, se da más información sobre el mismo problema:

 >>> f = open.(filename, 'rb') >>> data = bytearray(os.path.getsize(filename)) >>> f.readinto(data) 

Según el autor, no se crea una copia adicional y los datos se pueden modificar, ya que bytearray es mutable.

 stream = StringIO() for line in file: stream.write(line + "\n")