创建二进制流最简单的方法是在打开模式字符串时使用' b ':
f = open("myfile.jpg", "rb")
内存中的二进制流也可以作为BytesIO对象使用:
f = io.BytesIO(b"some initial binary data: \x00\x01")
通过 open
定义的 f
和通过 BytesIO
定义的 f
有何区别?换句话说,“内存中的二进制流”是什么,与 open
的操作有何不同?
为了简单起见,现在让我们只考虑写作,而不是阅读。
所以当你使用 open()
像这样说:
with open("test.dat", "wb") as f:
f.write(b"Hello World")
f.write(b"Hello World")
f.write(b"Hello World")
执行这个操作后,将创建一个名为test.dat
的文件,其中包含3个Hello World
。数据在写入文件后不会保留在内存中(除非由名称保存)。现在,如果您考虑使用io.BytesIO()
:with io.BytesIO() as f:
f.write(b"Hello World")
f.write(b"Hello World")
f.write(b"Hello World")
这段代码不是将内容写入文件,而是写入内存缓冲区,也就是一块RAM。换句话说,下面的代码与之等价:
buffer = b""
buffer += b"Hello World"
buffer += b"Hello World"
buffer += b"Hello World"
关于with语句的示例,最后也需要加上del buffer
。
这里的关键区别在于优化和性能。io.BytesIO
能够进行一些优化,使其比简单地逐个连接所有的 b"Hello World"
更快。
只是为了证明这一点,以下是一个小型基准测试:
import io
import time
begin = time.time()
buffer = b""
for i in range(0, 50000):
buffer += b"Hello World"
end = time.time()
seconds = end - begin
print("Concat:", seconds)
begin = time.time()
buffer = io.BytesIO()
for i in range(0, 50000):
buffer.write(b"Hello World")
end = time.time()
seconds = end - begin
print("BytesIO:", seconds)
除了性能提升之外,使用BytesIO
替换拼接的优点在于BytesIO
可以用作文件对象的替代品。因此,假设您有一个期望写入文件对象的函数。那么您可以将其内存缓冲区传递给该函数,而不是一个文件。
区别在于open(“myfile.jpg”,“rb”)
仅加载并返回myfile.jpg
的内容;而BytesIO
只是包含一些数据的缓冲区。
由于BytesIO
只是一个缓冲区,如果您想稍后将其内容写入文件,则必须执行以下操作:
buffer = io.BytesIO()
# ...
with open("test.dat", "wb") as f:
f.write(buffer.getvalue())
此外,您没有提到版本;我正在使用Python 3。关于示例:我使用的是with语句而不是调用f.close()
使用 open
函数可以打开硬盘上的文件。根据使用的模式,您可以从磁盘读取或写入(或同时进行读写)。
BytesIO
对象不与磁盘上的任何实际文件相关联,它只是一块行为类似于文件的内存块。它具有与从 open
返回的文件对象相同的 API(使用模式 r+b
,允许读取和写入二进制数据)。
BytesIO
(以及始终处于文本模式的近亲 StringIO
)在需要传递给期望得到文件对象的 API 但您希望直接传递数据时很有用。您可以在将数据传递给库之前将输入数据加载到 BytesIO
中。在函数返回后,您可以使用 getvalue()
方法从 BytesIO
中获取库写入文件的任何数据。当然,通常您只需要执行其中一个操作。