BytesIO.truncate 方法不扩展缓冲区内容

BytesIO.truncate method does not extend buffer contents

IOBase.truncate 方法的文档说:

truncate(size=None)

Resize the stream to the given size in bytes (or the current position if size is not specified). The current stream position isn’t changed. This resizing can extend or reduce the current file size. In case of extension, the contents of the new file area depend on the platform (on most systems, additional bytes are zero-filled). The new file size is returned.

Changed in version 3.5: Windows will now zero-fill files when extending.

因此,考虑到这一点,我假设 BytesIOBufferedIOBase 的子类又是 IOBase 的子类)在此之后更改其内部缓冲区大小方法已被调用。

但是下面的代码片段表明我的假设是错误的:

from io import BytesIO

# prints b'\x00\x00\x00\x00\x00\x00\x00\x00'
data = BytesIO(8 * b"\x00")
print(data.getvalue())

# prints 16
print(data.truncate(16))

# prints b'\x00\x00\x00\x00\x00\x00\x00\x00'
print(data.getvalue())

# prints b'\x00\x00\x00\x00\x00\x00\x00\x00'
print(bytes(data.getbuffer()))

我哪里转错了?

检查 source code,文档似乎不是最新的 BytesIO 实施:

static PyObject *_io_BytesIO_truncate_impl(bytesio *self, Py_ssize_t size)
/*[clinic end generated code: output=9ad17650c15fa09b input=423759dd42d2f7c1]*/
{
    CHECK_CLOSED(self);
    CHECK_EXPORTS(self);

    if (size < 0) {
        PyErr_Format(PyExc_ValueError,    
                     "negative size value %zd", size);
        return NULL;
    }

    if (size < self->string_size) {    
        self->string_size = size;    
        if (resize_buffer(self, size) < 0)    
            return NULL;   
    }

    return PyLong_FromSsize_t(size);

}

if (size < self->string_size) 测试确保如果大小大于先前的大小则不执行任何操作。

我的猜测是,对于真正的文件处理程序,truncate 的工作方式类似于底层平台(扩展文件),但与内存映射处理程序不同。

如果我们知道它会失败,则可以通过在对象末尾写入来非常简单地模拟所需的行为:

def my_truncate(data,size):
    current_size = len(data.getvalue())
    if size < current_size:
        return data.truncate(size)
    elif size == current_size:
        return size  # optim
    else:
        # store current position
        old_pos = data.tell()
        # go to end
        data.seek(current_size)
        # write zeroes
        data.write(b"\x00" * (size-current_size))
        # restore previous file position
        data.seek(old_pos)
        return size