将整个二进制文件读入Python
我需要从Python导入一个二进制文件——内容是有符号的16位整数,big-endian 下面的堆栈溢出问题建议如何一次提取几个字节,但这是放大到读取整个文件的方式吗将整个二进制文件读入Python,python,numpy,Python,Numpy,我需要从Python导入一个二进制文件——内容是有符号的16位整数,big-endian 下面的堆栈溢出问题建议如何一次提取几个字节,但这是放大到读取整个文件的方式吗 我想创建一个函数,如: from numpy import * import os def readmyfile(filename, bytes=2, endian='>h'): totalBytes = os.path.getsize(filename) values = empty(totalB
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字节)。有更好的方法吗?一次读取和解压2个字节
values[i] = struct.unpack(endian,f.read(bytes))[0]
为什么不一次读取1024字节呢?我会直接读取到EOF(这意味着检查是否接收到空字符串),这样就不需要使用range()和getsize了。
或者,使用
xrange
(而不是range
)应该可以改善情况,特别是在内存使用方面。此外,正如Falmari所说,同时读取更多数据将大大提高性能 也就是说,我不希望出现奇迹,因为我不确定列表是否是存储这么多数据的最有效方式。
使用NumPy的阵列和它的设施来实现这些功能怎么样?本节介绍如何使用numpyio.fread读取原始二进制文件。我相信这正是你需要的
注意:就个人而言,我从未使用过NumPy;然而,它的主要原因恰恰是处理大数据集——这就是你在问题中所做的。使用
numpy.fromfile
我认为你在这里遇到的瓶颈是双重的
根据您的操作系统和光盘控制器,对f.read(2)
的调用(如果f
是一个大文件)通常会得到有效的缓冲--。换句话说,操作系统将从光盘中读取一个或两个扇区(光盘扇区通常为数KB)到内存中,因为这并不比从该文件中读取2个字节贵多少。额外的字节被高效地缓存在内存中,以便下次调用读取该文件。不要依赖这种行为——这可能是你的瓶颈——但我认为这里还有其他问题
我更关心的是将单字节转换为对numpy的简短调用和单次调用。它们根本不被缓存。您可以在Python的int列表中保留所有的简短内容,并在需要时(如果需要的话)将整个列表转换为numpy。您还可以从进行一次调用struct.unpack\u,将缓冲区中的所有内容转换为一次一个简短的内容
考虑:
#!/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!"
如果您不需要短裤是numpy,此函数将在6秒内运行。所有这些都是在OS X、python 2.6.1 64位、2.93 gHz核心i7、8 GB ram上实现的。如果将read\u wopper
中的buf\u size=1024*2
更改为buf\u size=2**16
运行时间为:
real 0m10.810s
user 0m10.156s
sys 0m0.651s
所以,我认为,您的主要瓶颈是解包的单字节调用,而不是从光盘读取的2字节数据。您可能希望确保您的数据文件没有碎片化,并且如果您使用的是OS X,则您的(和)没有碎片化
编辑我发布了完整的代码来创建并读取一个int的二进制文件。在我的iMac上,读取随机整数文件的时间始终小于15秒。它需要大约1:23的时间来创造,因为创造是一次短一个 我也遇到了同样的问题,尽管在我的特殊情况下,我不得不转换一个非常奇怪的二进制格式(500MB)文件,其中包含166个元素的交错块,这些元素是3字节有符号整数;因此,我也遇到了从24位到32位有符号整数的转换问题,这会稍微降低速度
我已经使用NumPy的memmap(这只是使用Python的memmap的一种简便方法)和struct.unpack在文件的大部分上解决了这个问题
有了这个解决方案,我可以在大约90秒内转换(读、写、写)整个文件(用time.clock()计时)
我可以上传部分代码。我认为速度很慢,因为bytes=2
。读取150mb的文件会很慢。你期待什么?它有多慢?实际上只需要3.5分钟(根据unixtime
),但我可以使用readBin
(我有数千个这样的文件…)在不到一分钟的时间内将其读入R。数据是清晰的二进制还是16位数字的ASCII表示?如果我这样做,它怎么知道它是一个16位整数而不是32位或其他什么?…它给了我一个错误:TypeError:Struct()参数1必须是string,而不是int
我认为Struct.unpack不是最好的解决方案。这意味着要接受字符串,而不是二进制文件。我将研究NumPy,但加载后如何解析它?或者在循环中解析它?谢谢~在上面提供的链接中有一节介绍如何使用fread读取二进制文件。我将更新原始答案,以便更好地说明这一点。谢谢--该链接非常有用。它是否像fromfile(filename,dtype='>i2')
一样简单?@Stephen,是的,这就是您需要做的所有事情。如果Karl在答案中提到了这一点,那么这是最好最简单的答案。根据我的经验,numpy.fromfile非常快速且易于使用。谢谢--明天将尝试此功能,尽管目前正在使用numpy.filefrom--但这对于未安装numpy的机器来说可能非常好(这对于我管理的所有不同的机器来说都不是微不足道的)!嗯…仍然是2M50(OS X,Python 2.6 64位,4GB内存)…感谢您对缓存的深入了解。'~@Stephen:您的光盘有什么不寻常的地方吗?光盘格式是NTFS还是完全的还是碎片化的?如果是NTFS,OS X NTFS驱动程序速度不快。我将发布完整的代码,并在相对空的HFS驱动器上试用……奇怪的是,它是OS X扩展的(日志记录)但是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 sys 0m1.509soops,格式不好——无论如何,wri
real 0m10.810s
user 0m10.156s
sys 0m0.651s