将整个二进制文件读入 Python
Posted
技术标签:
【中文标题】将整个二进制文件读入 Python【英文标题】:Reading an entire binary file into Python 【发布时间】:2010-12-12 19:47:04 【问题描述】:我需要从 Python 导入一个二进制文件——内容是有符号的 16 位整数,大端。
以下 Stack Overflow 问题建议如何一次提取多个字节,但这是扩大读取整个文件的方法吗?
Reading some binary file in Python
Receiving 16-bit integers in Python
我想创建一个类似的函数:
from numpy import *
import os
def readmyfile(filename, bytes=2, endian='>h'):
totalBytes = os.path.getsize(filename)
values = empty(totalBytes/bytes)
with open(filename, 'rb') as f:
for i in range(len(values)):
values[i] = struct.unpack(endian, f.read(bytes))[0]
return values
filecontents = readmyfile('filename')
但这很慢(文件为 165924350 字节)。有没有更好的办法?
【问题讨论】:
我认为它很慢,因为bytes=2
。
读取 150mb 的文件会很慢。你能指望什么?有多慢?
实际上只有大约 3.5 分钟(根据 unix time
),但我可以使用 readBin
在不到一分钟的时间内将其读入 R
(我有数千个这样的文件...)
数据是明确的二进制还是16位数字的ASCII表示?
【参考方案1】:
使用numpy.fromfile
。
【讨论】:
有fromfile(filename,dtype='>i2')
这么简单吗?
@Stephen,是的,这就是你需要做的。如果 Karl 把它放在答案中,那么这是最好和最简单的答案。
根据我的经验 numpy.fromfile 非常快且非常易于使用。【参考方案2】:
我会直接读取直到 EOF(这意味着检查是否接收到空字符串),然后不再需要使用 range() 和 getsize。
或者,使用xrange
(而不是range
)应该会有所改善,尤其是在内存使用方面。
此外,正如 Falmarri 所建议的,同时读取更多数据会大大提高性能。
也就是说,我不会期待奇迹,因为我不确定列表是存储所有数据量的最有效方式。 使用 NumPy 的数组及其对read/write binary files 的设施怎么样?在这个link 中有一节关于使用 numpyio.fread 读取原始二进制文件。我相信这应该正是您所需要的。
注意:就我个人而言,我从未使用过 NumPy;然而,它的主要存在理由正是处理大量数据 - 这就是您在问题中所做的。
【讨论】:
我会研究 NumPy,但是一旦它被加载,我该如何解析它呢?还是循环解析它?谢谢~ 在上面提供的链接中有一个关于使用 fread 读取二进制文件的部分。我将更新原始答案,以更好地说明这一点。 谢谢——那个链接很有帮助。【参考方案3】:您一次读取和解压 2 个字节
values[i] = struct.unpack(endian,f.read(bytes))[0]
您为什么不一次读取 1024 个字节?
【讨论】:
如果我这样做,它怎么知道它是一个 16 位整数而不是 32 或其他什么? ...它给了我一个错误:TypeError: Struct() argument 1 must be string, not int
我不认为 struct.unpack 是这里最好的解决方案。这意味着接收字符串,而不是二进制文件。【参考方案4】:
我也遇到过同样的问题,尽管在我的特殊情况下,我不得不转换一个非常奇怪的二进制格式 (500 MB) 文件,其中包含 166 个元素的交错块,这些元素是 3 字节有符号整数;所以我也遇到了从 24 位转换为 32 位有符号整数的问题,这会减慢速度。
我已经使用 NumPy 的 memmap(这只是使用 Python 的 memmap 的一种便捷方式)和 struct.unpack 对大块文件进行了解决。
使用此解决方案,我可以在大约 90 秒内(使用 time.clock() 计时)转换(读取、执行操作和写入磁盘)整个文件。
我可以上传部分代码。
【讨论】:
【参考方案5】:我认为您在这里遇到的瓶颈是双重的。
根据您的操作系统和磁盘控制器,对f.read(2)
和f
是一个大文件的调用通常会被有效缓冲——usually。换句话说,操作系统会将磁盘上的一个或两个扇区(磁盘扇区通常为几 KB)读取到内存中,因为这并不比从该文件中读取 2 个字节贵很多。额外的字节被有效地缓存在内存中,为下一次调用读取该文件做好准备。不要依赖这种行为——这可能是你的瓶颈——但我认为这里还有其他问题。
我更关心将单字节转换为对 numpy 的短而单次调用。这些根本没有缓存。您可以将所有短裤保存在一个 Python 整数列表中,并在需要时(如果需要)将整个列表转换为 numpy。您还可以拨打一个电话 struct.unpack_from
来转换缓冲区中的所有内容,而不是一次转换一个短内容。
考虑:
#!/usr/bin/python
import random
import os
import struct
import numpy
import ctypes
def read_wopper(filename,bytes=2,endian='>h'):
buf_size=1024*2
buf=ctypes.create_string_buffer(buf_size)
new_buf=[]
with open(filename,'rb') as f:
while True:
st=f.read(buf_size)
l=len(st)
if l==0:
break
fmt=endian[0]+str(l/bytes)+endian[1]
new_buf+=(struct.unpack_from(fmt,st))
na=numpy.array(new_buf)
return na
fn='bigintfile'
def createmyfile(filename):
bytes=165924350
endian='>h'
f=open(filename,"wb")
count=0
try:
for int in range(0,bytes/2):
# The first 32,767 values are [0,1,2..0x7FFF]
# to allow testing the read values with new_buf[value<0x7FFF]
value=count if count<0x7FFF else random.randint(-32767,32767)
count+=1
f.write(struct.pack(endian,value&0x7FFF))
except IOError:
print "file error"
finally:
f.close()
if not os.path.exists(fn):
print "creating file, don't count this..."
createmyfile(fn)
else:
read_wopper(fn)
print "Done!"
我创建了一个 165,924,350 字节 (158.24 MB) 的随机短裤签名整数文件,相当于 82,962,175 个带符号的 2 字节短裤。使用这个文件,我运行了上面的read_wopper
函数,它运行在:
real 0m15.846s
user 0m12.416s
sys 0m3.426s
如果你不需要短裤是 numpy,这个函数在 6 秒内运行。所有这些都在 OS X、python 2.6.1 64 位、2.93 GHz Core i7、8 GB 内存上。如果将read_wopper
中的buf_size=1024*2
更改为buf_size=2**16
,则运行时间为:
real 0m10.810s
user 0m10.156s
sys 0m0.651s
所以我认为你的主要瓶颈是单字节调用解包 - 而不是你从磁盘读取的 2 字节。您可能需要确保您的数据文件没有碎片,如果您使用的是 OS X,您的free disc space(和here)没有碎片。
编辑我发布了完整的代码来创建然后读取整数的二进制文件。在我的 iMac 上,读取随机整数文件的时间始终小于 15 秒。由于一次创作是短暂的,因此创作大约需要 1:23。
【讨论】:
谢谢——明天会试试这个,虽然目前正在使用 numpy.filefrom——但这对于没有安装 numpy 的机器来说可能很棒(这对于我管理的所有不同的机器来说都不是微不足道的)! 嗯...仍然是 2 分 50 秒(OS X,Python 2.6 64 位,4GB 内存)...感谢您对缓存的见解~ @Stephen:你的光盘有什么不寻常的地方吗?光盘格式是 NTFS 还是真的满了还是零散了?如果是 NTFS,则 OS X NTFS 驱动程序并不快。我将发布我的完整代码,并在相对空的 HFS 驱动器上尝试... 奇怪,它是 OS X Extended (Journaled) 但是 bash-3.2$ time python test.py 创建文件,不要计算这个...真正的 2m28.376s 用户 2m6.882s sys 0m3.664s bash-3.2$ time python test.py 完成!真实 0m28.485s 用户 0m23.273s 系统 0m1.509s 哎呀,格式不太好——无论如何,写的时间更长,但阅读的速度更快。我想知道二进制文件是否有不同之处。 Unix 头似乎在 bigintfile 上冻结(?),而在我的其他文件上却没有。感谢您的所有意见...以上是关于将整个二进制文件读入 Python的主要内容,如果未能解决你的问题,请参考以下文章