这是一个错误吗?它演示了当您使用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,我也很感激。
答案 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().