这是一个python 3文件错误?

时间:2014-08-29 18:57:03

标签: python python-3.2

这是一个错误吗?它演示了当您使用libtiff从打开的tiff文件句柄中提取图像时会发生什么。它适用于python 2.x,不适用于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()

我觉得错误的输出是: 预计284,实际-504

取消注释fp1.tell()会产生一些......副作用......这会稳定py3句柄,我不知道为什么。如果有人可以测试其他版本的python3,我也很感激。

2 个答案:

答案 0 :(得分:5)

不,这不是错误。 Python 3 io library为您提供open()调用的文件对象,为您提供缓冲文件对象。对于二进制文件,您将获得一个(子类)io.BufferedIOBase

Python 2文件对象更原始,虽然你也可以使用io library

通过在操作系统级别搜索,您正在绕过缓冲区并且正在破坏内部状态。一般来说,正如医生对病人说抱怨他的皮肤疼痛一样: 不要这样做

如果您迫切需要这样做,至少要通过io.RawIOBase class使用基础原始文件对象(io.BufferedIO.raw attribute的子类):

fp1 = open(filename, "rb").raw

答案 1 :(得分:4)

os.dup创建一个重复的文件描述符,引用相同的打开文件描述。因此,os.lseek(fd, 28, SEEK_SET)会更改fp1下的文件的搜索位置。

Python的文件对象缓存文件位置以避免重复的系统调用。这样做的副作用是在不使用文件对象方法的情况下更改文件位置将使缓存位置和实际位置失去同步,从而导致像您观察到的那样无意义。

更糟糕的是,因为文件是由Python内部缓冲的,所以在文件外部寻找方法实际上可能导致返回的文件数据不正确,从而导致损坏或其他令人讨厌的东西。

bufferedio.c中的文档说明tell可用于重新初始化缓存的值:

* 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().