Python io.BytesIO的write()、read()和getvalue()方法是如何工作的?

40

我试图理解 io.BytesIOwrite()read() 方法。我的理解是,我可以像使用文件对象一样使用 io.BytesIO

import io
in_memory = io.BytesIO(b'hello')
print( in_memory.read() )

上面的代码将如预期地返回b'hello',但下面的代码将返回空字符串b''

import io
in_memory = io.BytesIO(b'hello')
in_memory.write(b' world')
print( in_memory.read() )

我的问题如下:

- io.BytesIO.write(b' world') 究竟是在做什么?

- io.BytesIO.read()io.BytesIO.getvalue() 有什么区别?

我猜想答案与 io.BytesIO 是一个流对象有关,但总体情况对我来说不是很清晰。

3个回答

39
问题在于你的位置位于流的末尾。把位置想象成游标。一旦你写入了b' world',你的游标就在流的末尾。当你尝试使用.read()时,你正在读取游标位置之后的所有内容——也就是什么都没有,所以你得到了一个空的字节串。
为了在流中导航,你可以使用.seek方法:
>>> import io
>>> in_memory = io.BytesIO(b'hello', )
>>> in_memory.write(b' world')
>>> in_memory.seek(0)  # go to the start of the stream
>>> print(in_memory.read())
b' world'

请注意,就像在写入模式('w')下的文件流一样,初始字节 b'hello' 已被您写入的 b' world' 覆盖。

.getvalue() 只会返回流的整个内容,不考虑当前位置。


12

这是一个内存流,但仍然是一种流。位置被存储,因此像任何其他流一样,如果您在写入后尝试读取,您必须重新定位:

import io
in_memory = io.BytesIO(b'hello')
in_memory.seek(0,2)   # seek to end, else we overwrite
in_memory.write(b' world')
in_memory.seek(0)    # seek to start
print( in_memory.read() )

输出:

b'hello world'

当使用 in_memory.getvalue() 时,不需要最后的 seek(0),因为它会从位置0返回流的内容。


8

BytesIO 就像一个文件一样,但你可以同时读写它。可能让人困惑的是,读写位置是相同的。因此,首先你需要:

in_memory = io.BytesIO(b'hello')

这将在in_memory中为您提供一个字节缓冲区,内容为b'hello',读/写位置位于开头(在第一个b'h'之前)。 当您执行以下操作时:

in_memory.write(b' world')

您实际上正在用 b' world' 覆盖 b'hello'(并且实际上多了一个字节),现在您已经到达结尾位置(b'd' 后面),所以当您执行以下操作时:

print( in_memory.read() )

你看不到任何内容是因为当前位置之后没有可读的内容。但是,你可以使用seek来移动位置,所以如果你这样做:

import io
in_memory = io.BytesIO(b'hello')
in_memory.write(b' world')
in_memory.seek(0)
print( in_memory.read() )

你会得到:
b' world'

请注意,您看不到初始的b'hello',因为它被覆盖了。如果您想在初始内容之后写入一些内容,您可以先定位到结尾:
import io
in_memory = io.BytesIO(b'hello')
in_memory.seek(0, 2)
in_memory.write(b' world')
in_memory.seek(0)
print( in_memory.read() )

输出:

b'hello world'

编辑:关于getvalue,正如其他答案所指出的那样,它会给您提供完整的内部缓冲区,而不受当前位置的影响。这个操作显然对于文件是不可用的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接