我需要从Python导入一个二进制文件 - 内容是16位整数的标记,大端。
以下Stack Overflow问题建议如何一次拉入几个字节,但这是扩展读取整个文件的方法吗?
我想创建一个像:
这样的函数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字节)。还有更好的方法吗?
答案 0 :(得分:10)
使用numpy.fromfile
。
答案 1 :(得分:4)
我会直接阅读直到EOF(这意味着检查接收一个空字符串),然后删除然后需要使用range()和getsize。
或者,使用xrange
(而不是range
)可以改善一些事情,尤其是在内存使用方面
此外,正如Falmarri所建议的那样,同时阅读更多数据会大大提高性能。
那就是说,我不会指望奇迹,也因为我不确定列表是存储所有数据量的最有效方式。
如何使用NumPy的数组及其设施read/write binary files?在这个link中,有一节介绍如何使用numpyio.fread读取原始二进制文件。我相信这应该是你所需要的。
注意:就个人而言,我从未使用过NumPy;然而,它的主要存在理由是处理大量数据 - 这就是你在问题中所做的。
答案 2 :(得分:2)
您正在一次读取和解包2个字节
values[i] = struct.unpack(endian,f.read(bytes))[0]
为什么不一次读取1024个字节?
答案 3 :(得分:2)
我遇到了同样的问题,虽然在我的特殊情况下,我不得不转换一个非常奇怪的二进制格式(500 MB)文件,其中包含166个元素的隔行扫描块,这些块是3字节有符号整数;所以我也遇到了从24位转换为32位有符号整数的问题,这会让事情变得缓慢。
我已经使用NumPy的memmap(它只是使用Python的memmap的一种方便方法)和struct.unpack在文件的大块上解决了它。
使用此解决方案,我能够在大约90秒内(使用time.clock()定时)将整个文件转换(读取,执行操作和写入磁盘)。
我可以上传部分代码。
答案 4 :(得分:1)
我认为你在这里遇到的瓶颈是双重的。
根据您的操作系统和光盘控制器,f.read(2)
f
作为一个重要文件的调用通常会被有效缓冲 - usually。换句话说,操作系统会将光盘中的一个或两个扇区(光盘扇区通常为几KB)读入内存,因为这并不比从该文件读取2个字节贵得多。额外的字节在内存中高效缓存,准备下次调用读取该文件。不要依赖这种行为 - 这可能是你的瓶颈 - 但我认为这里还有其他问题。
我更关注单字节转换为短暂和单次调用numpy。这些都没有缓存。您可以将所有短路保存在Python的int列表中,并在需要时(如果需要)将整个列表转换为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内存。如果您将buf_size=1024*2
中的read_wopper
更改为buf_size=2**16
,则运行时间为:
real 0m10.810s
user 0m10.156s
sys 0m0.651s
因此,我认为你的主要瓶颈是单字节调用解包 - 而不是你从光盘读取的2字节。您可能希望确保数据文件不会碎片化,如果您使用OS X,则free disc space(和here)不会碎片化。
编辑我发布了完整的代码来创建然后读取整数的二进制文件。在我的iMac上,我一直得到&lt; 15秒读取随机整数文件。创建大约需要1:23,因为创建一次只有一个短。