【问题标题】:Confusing about StringIO, cStringIO and ByteIO对 StringIO、StringIO 和 BytesIO 感到困惑
【发布时间】:2023-04-11 10:18:01
【问题描述】:

我已经用谷歌搜索并在 SO 上搜索这些缓冲区模块之间的区别。不过,我还是不太明白,我觉得我看的一些帖子已经过时了。

在 Python 2.7.11 中,我使用 r = requests.get(url) 下载了一个特定格式的二进制文件。然后我将StringIO.StringIO(r.content)cStringIO.StringIO(r.content)io.BytesIO(r.content) 传递给为解析内容而设计的函数。

所有这三种方法都可用。我的意思是,即使文件是二进制文件,使用StringIO 仍然可行。为什么?

另一件事是关于他们的效率。

In [1]: import StringIO, cStringIO, io

In [2]: from numpy import random

In [3]: x = random.random(1000000)

In [4]: %timeit y = cStringIO.StringIO(x)
1000000 loops, best of 3: 736 ns per loop

In [5]: %timeit y = StringIO.StringIO(x)
1000 loops, best of 3: 283 µs per loop

In [6]: %timeit y = io.BytesIO(x)
1000 loops, best of 3: 1.26 ms per loop

如上图所示,cStringIO > StringIO > BytesIO

我发现有人提到io.BytesIO 总是制作一个新副本,这会花费更多时间。但也有一些帖子提到,这在以后的 Python 版本中已修复。

那么,任何人都可以在最新的 Python 2.x 和 3.x 中对这些 IOs 进行彻底的比较吗?


我找到的一些参考资料:

  • https://trac.edgewall.org/ticket/12046

    io.StringIO 需要一个 unicode 字符串。 io.BytesIO 需要一个字节字符串。 StringIO.StringIO 允许 unicode 或字节字符串。 cStringIO.StringIO 需要一个被编码为字节字符串的字符串。

cStringIO.StringIO('abc') 不会引发任何错误。

2014 年这篇文章中有一个修复补丁。

  • 这里没有列出很多 SO 帖子。

这是 Eric 示例的 Python 2.7 结果

%timeit cStringIO.StringIO(u_data)
1000000 loops, best of 3: 488 ns per loop
%timeit cStringIO.StringIO(b_data)
1000000 loops, best of 3: 448 ns per loop
%timeit StringIO.StringIO(u_data)
1000000 loops, best of 3: 1.15 µs per loop
%timeit StringIO.StringIO(b_data)
1000000 loops, best of 3: 1.19 µs per loop
%timeit io.StringIO(u_data)
1000 loops, best of 3: 304 µs per loop
# %timeit io.StringIO(b_data)
# error
# %timeit io.BytesIO(u_data)
# error
%timeit io.BytesIO(b_data)
10000 loops, best of 3: 77.5 µs per loop

对于 2.7,cStringIO.StringIOStringIO.StringIO 的效率远远高于 io

【问题讨论】:

  • 你能把你的每个 sn-ps 标记为 python 2 还是 python 3 吗?
  • @Eric,我在 Python 2.7.11 中完成了所有测试。好像(c)StringIO在3中被io替换了,我主要用2.7。但我认为其他读者讨论这两个版本会很有意义。
  • io 也在 python 2 中

标签: python stringio bytesio cstringio


【解决方案1】:

您应该使用io.StringIO 处理unicode 对象和io.BytesIO 处理python 2 和3 中的bytes 对象,以实现前向兼容性(这是所有3 必须提供的)。


这是一个更好的测试(针对 python 2 和 3),不包括从 numpy 到 str/bytes的转换成本

import numpy as np
import string
b_data = np.random.choice(list(string.printable), size=1000000).tobytes()
u_data = b_data.decode('ascii')
u_data = u'\u2603' + u_data[1:]  # add a non-ascii character

然后:

import io
%timeit io.StringIO(u_data)
%timeit io.StringIO(b_data)
%timeit io.BytesIO(u_data)
%timeit io.BytesIO(b_data)

在python 2中也可以测试:

import StringIO, cStringIO
%timeit cStringIO.StringIO(u_data)
%timeit cStringIO.StringIO(b_data)
%timeit StringIO.StringIO(u_data)
%timeit StringIO.StringIO(b_data)

其中一些会崩溃,抱怨非 ascii 字符


Python 3.5 结果:

>>> %timeit io.StringIO(u_data)
100 loops, best of 3: 8.61 ms per loop
>>> %timeit io.StringIO(b_data)
TypeError: initial_value must be str or None, not bytes
>>> %timeit io.BytesIO(u_data)
TypeError: a bytes-like object is required, not 'str'
>>> %timeit io.BytesIO(b_data)
The slowest run took 6.79 times longer than the fastest. This could mean that an intermediate result is being cached
1000000 loops, best of 3: 344 ns per loop

Python 2.7 结果(在不同的机器上运行):

>>> %timeit io.StringIO(u_data)
1000 loops, best of 3: 304 µs per loop
>>> %timeit io.StringIO(b_data)
TypeError: initial_value must be unicode or None, not str
>>> %timeit io.BytesIO(u_data)
TypeError: 'unicode' does not have the buffer interface
>>> %timeit io.BytesIO(b_data)
10000 loops, best of 3: 77.5 µs per loop
>>> %timeit cStringIO.StringIO(u_data)
UnicodeEncodeError: 'ascii' codec cant encode character u'\u2603' in position 0: ordinal not in range(128)
>>> %timeit cStringIO.StringIO(b_data)
1000000 loops, best of 3: 448 ns per loop
>>> %timeit StringIO.StringIO(u_data)
1000000 loops, best of 3: 1.15 µs per loop
>>> %timeit StringIO.StringIO(b_data)
1000000 loops, best of 3: 1.19 µs per loop

【讨论】:

  • 因此,在 3.x 中,BytesIOStringIO 不同并且比在 2.x 中快得多。
  • io.BytesIOio.StringIO 没有可比性,因为一个只适用于二进制输入,另一个只适用于 unicode 字符串
  • @Lee:没错,这就是我所期望的。 cStringIOunicode 数据上使用是不安全的,因为它可能在运行时失败,具体取决于值。
  • 不@stormfield,它并不安全。一个用于str,另一个用于bytes。根据您要写入的数据类型进行选择,而不是速度。
  • 这些时间测试不是很有帮助,因为您只测量创建StringIO 对象的时间而不是使用它的时间,这才是真正重要的。更好的测试是执行多个reads 或调用诸如readlines() 之类的函数。
猜你喜欢
  • 1970-01-01
  • 2021-11-28
  • 1970-01-01
  • 2013-03-29
  • 2014-11-02
  • 1970-01-01
  • 2014-03-18
  • 2012-10-08
  • 1970-01-01
相关资源
最近更新 更多