2024-04-19 10:33:01 发布
网友
在Python中,我有一个文件流,我想将其中的一部分复制到一个StringIO中。我希望这是最快的,用最少的副本。
StringIO
但如果我这样做了:
data = file.read(SIZE) stream = StringIO(data)
我想两份已经完成了,不是吗?一份拷贝到文件中的数据,另一份拷贝到内部缓冲区中的StringIO。我能避开其中一份吗?我不需要临时的data,所以我认为一份就足够了
data
不,没有多余的复印件。用于存储数据的缓冲区是相同的。对于同一数据,data和使用StringIO.getvalue()可访问的内部属性都是不同的名称。
StringIO.getvalue()
Python 2.7 (r27:82500, Jul 30 2010, 07:39:35) [GCC 4.1.2 20080704 (Red Hat 4.1.2-48)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import StringIO >>> data = open("/dev/zero").read(1024) >>> hex(id(data)) '0xea516f0' >>> stream = StringIO.StringIO(data) >>> hex(id(stream.getvalue())) '0xea516f0'
快速浏览一下the source可以发现cStringIO在构造时也不会生成副本,但在调用cStringIO.getvalue()时它确实会生成副本,因此我不能重复上面的演示。
cStringIO
cStringIO.getvalue()
也许你要找的是一个buffer/memoryview:
>>> data = file.read(SIZE) >>> buf = buffer(data, 0, len(data))
这样,您就可以访问原始数据的一部分,而无需复制它。但是,您必须对只以面向字节的格式访问该数据感兴趣,因为缓冲区协议提供了这种格式。
您可以在相关的question中找到更多信息。
编辑:在我通过reddit找到的blog post中,提供了关于同一问题的更多信息:
>>> f = open.(filename, 'rb') >>> data = bytearray(os.path.getsize(filename)) >>> f.readinto(data)
根据作者的说法,由于bytearray是可变的,因此不会创建额外的副本和修改数据。
bytearray
简而言之:使用StringIO不能避免两个副本。
一些假设:
file.read(SOME_BYTE_COUNT)
Long answer:由于python字符串是不可变的,而StringIO缓冲区是不可变的,因此迟早要进行复制;否则您将要更改不可变对象!对于您希望实现的功能,StringIO对象需要有一个专用的方法,该方法直接从作为参数给定的文件对象中读取数据。没有这种方法。
在StringIO的之外,有一些解决方案可以避免额外的复制。在我的头顶上,这将直接读取一个文件到一个可修改的字节数组中,没有额外的副本:
import numpy as np a = np.fromfile("filename.ext", dtype="uint8")
使用它可能会很麻烦,这取决于您想要的用法,因为它是一个从0到255的值数组,而不是一个字符数组。但它在功能上等同于StringIO对象,使用np.fromstring、np.tostring、np.tofile和切片符号应该可以让您达到所需的效果。您可能还需要np.insert、np.delete和np.append。
np.fromstring
np.tostring
np.tofile
np.insert
np.delete
np.append
我相信还有其他模块也会做类似的事情。
时间:
这些到底有多重要?好吧,让我看看。我做了一个100MB的文件,largefile.bin。然后我使用两种方法读入文件并更改第一个字节。
largefile.bin
$ python -m timeit -s "import numpy as np" "a = np.fromfile('largefile.bin', 'uint8'); a[0] = 1" 10 loops, best of 3: 132 msec per loop $ python -m timeit -s "from cStringIO import StringIO" "a = StringIO(); a.write(open('largefile.bin').read()); a.seek(0); a.write('1')" 10 loops, best of 3: 203 msec per loop
所以在我的例子中,使用StringIO比使用numpy慢50%。
最后,为了进行比较,直接编辑文件:
$ python -m timeit "a = open('largefile.bin', 'r+b'); a.seek(0); a.write('1')" 10000 loops, best of 3: 29.5 usec per loop
所以,速度快了近4500倍。当然,这完全取决于你要对文件做什么。改变第一个字节几乎不具有代表性。但是使用这种方法,你确实可以在其他两个操作系统上抢先一步,而且由于大多数操作系统都有很好的磁盘缓冲,所以速度也可能非常好。
(如果不允许编辑文件,因此希望避免制作工作副本的成本,有两种可能的方法可以提高速度。如果您可以选择文件系统,Btrfs有一个copy-on-write文件复制操作——使获取文件副本的行为几乎是即时的。使用任何文件系统的LVM快照也可以达到同样的效果。)
不,没有多余的复印件。用于存储数据的缓冲区是相同的。对于同一数据,
data
和使用StringIO.getvalue()
可访问的内部属性都是不同的名称。快速浏览一下the source可以发现
cStringIO
在构造时也不会生成副本,但在调用cStringIO.getvalue()
时它确实会生成副本,因此我不能重复上面的演示。也许你要找的是一个buffer/memoryview:
这样,您就可以访问原始数据的一部分,而无需复制它。但是,您必须对只以面向字节的格式访问该数据感兴趣,因为缓冲区协议提供了这种格式。
您可以在相关的question中找到更多信息。
编辑:在我通过reddit找到的blog post中,提供了关于同一问题的更多信息:
根据作者的说法,由于
bytearray
是可变的,因此不会创建额外的副本和修改数据。简而言之:使用StringIO不能避免两个副本。
一些假设:
file.read(SOME_BYTE_COUNT)
变量。Long answer:由于python字符串是不可变的,而StringIO缓冲区是不可变的,因此迟早要进行复制;否则您将要更改不可变对象!对于您希望实现的功能,StringIO对象需要有一个专用的方法,该方法直接从作为参数给定的文件对象中读取数据。没有这种方法。
在StringIO的之外,有一些解决方案可以避免额外的复制。在我的头顶上,这将直接读取一个文件到一个可修改的字节数组中,没有额外的副本:
使用它可能会很麻烦,这取决于您想要的用法,因为它是一个从0到255的值数组,而不是一个字符数组。但它在功能上等同于StringIO对象,使用
np.fromstring
、np.tostring
、np.tofile
和切片符号应该可以让您达到所需的效果。您可能还需要np.insert
、np.delete
和np.append
。我相信还有其他模块也会做类似的事情。
时间:
这些到底有多重要?好吧,让我看看。我做了一个100MB的文件,
largefile.bin
。然后我使用两种方法读入文件并更改第一个字节。所以在我的例子中,使用StringIO比使用numpy慢50%。
最后,为了进行比较,直接编辑文件:
所以,速度快了近4500倍。当然,这完全取决于你要对文件做什么。改变第一个字节几乎不具有代表性。但是使用这种方法,你确实可以在其他两个操作系统上抢先一步,而且由于大多数操作系统都有很好的磁盘缓冲,所以速度也可能非常好。
(如果不允许编辑文件,因此希望避免制作工作副本的成本,有两种可能的方法可以提高速度。如果您可以选择文件系统,Btrfs有一个copy-on-write文件复制操作——使获取文件副本的行为几乎是即时的。使用任何文件系统的LVM快照也可以达到同样的效果。)
相关问题 更多 >
编程相关推荐