¿Es esto un error? Demuestra lo que sucede cuando usa libtiff para extraer una imagen de un identificador de archivo tiff abierto. Funciona en python 2.xy no funciona en python 3.2.3

import os

# any file will work here, since it's not actually loading the tiff
# assuming it's big enough for the seek
filename = "/home/kostrom/git/wiredfool-pillow/Tests/images/multipage.tiff"

def test():
    fp1 = open(filename, "rb")
    buf1 = fp1.read(8)
    fp1.seek(28)
    fp1.read(2)
    for x in range(16):
        fp1.read(12)
    fp1.read(4)

    fd = os.dup(fp1.fileno())
    os.lseek(fd, 28, os.SEEK_SET)
    os.close(fd)

    # this magically fixes it: fp1.tell()
    fp1.seek(284)
    expect_284 = fp1.tell()
    print ("expected 284, actual %d" % expect_284)

test()

El resultado que siento que es un error es: esperado 284, real -504

Descomentar el fp1.tell () produce algunos ... efectos secundarios ... que estabilizan el controlador py3, y no sé por qué. También agradecería si alguien puede probar otras versiones de python3.

1
Kenny Ostrom 29 ago. 2014 a las 22:57

2 respuestas

La mejor respuesta

os.dup crea un descriptor de archivo duplicado que se refiere a la misma descripción de archivo abierto. Por lo tanto, os.lseek(fd, 28, SEEK_SET) cambia la posición de búsqueda del archivo subyacente fp1.

Los objetos de archivo de Python almacenan en caché la posición del archivo para evitar repetidas llamadas al sistema. El efecto secundario de esto es que cambiar la posición del archivo sin usar los métodos del objeto del archivo desincronizará la posición en caché y la posición real, lo que dará lugar a tonterías como las que ha observado.

Peor aún, debido a que Python almacena internamente los archivos, buscar fuera de los métodos de archivo podría causar que los datos devueltos sean incorrectos, lo que provocaría corrupción u otras cosas desagradables.

La documentación en bufferedio.c notas que tell se puede usar para reinicializar el valor almacenado en caché:

* The absolute position of the raw stream is cached, if possible, in the
  `abs_pos` member. It must be updated every time an operation is done
  on the raw stream. If not sure, it can be reinitialized by calling
  _buffered_raw_tell(), which queries the raw stream (_buffered_raw_seek()
  also does it). To read it, use RAW_TELL().
4
nneonneo 29 ago. 2014 a las 19:22

No, esto no es un error. La io biblioteca de Python 3, que le proporciona el objeto de archivo de una llamada open(), le proporciona un objeto de archivo almacenado en la memoria intermedia . Para los archivos binarios, se le da una (subclase de) io.BufferedIOBase.

El objeto de archivo Python 2 es mucho más primitivo, aunque puede usar io biblioteca allí también.

Al buscar a nivel del sistema operativo, está pasando por alto el búfer y está eliminando el estado interno. En términos generales, como el médico le dijo al paciente quejándose de que pellizcarse la piel duele: no hagas eso .

Si tiene una necesidad apremiante de hacer esto de todos modos, al menos use el objeto de archivo sin procesar subyacente (una subclase de io.RawIOBase clase) a través de atributo io.BufferedIO.raw:

fp1 = open(filename, "rb").raw
5
Martijn Pieters 29 ago. 2014 a las 19:16