【问题标题】:Is this a python 3 file bug?这是python 3文件错误吗?
【发布时间】:2014-08-29 18:57:03
【问题描述】:

这是一个错误吗?它演示了当您使用 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,我也将不胜感激。

【问题讨论】:

  • 您没有告诉我们发生的事情与您的预期不同。

标签: python file python-3.2


【解决方案1】:

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

Python 2 文件对象要原始得多,尽管您可以在那里也使用io library

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

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

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

【讨论】:

  • 您可以通过访问二进制文件的f.raw 和文本文件的f.buffer.raw 来获取底层的RawIOBase 对象(在本例中为FileIO)。当然,您仍然非常绝对不应该将 FileIO 对象上的方法与其 fd 上的 os 函数混用,但至少不会那么令人惊讶……
【解决方案2】:

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

【讨论】:

  • 这解释了为什么 tell 修复了它,但类似 ​​flush 没有。感谢您没有放弃调用像 libtiff(和许多其他)这样成熟的代码库的需要。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-23
相关资源
最近更新 更多