遍历二进制文件的惯用方法是什么?
对于一个文本文件,我可以这样写:
with open(path, 'r') as file:
for line in file:
# handle the line
这和下面这个是一样的:
with open(path, 'r') as file:
for line in iter(file.readline, ''):
# handle the line
这个写法在 PEP 234 中有说明,但我找不到类似的写法适用于二进制文件。
对于一个二进制文件,我可以这样写:
with open(path, 'rb') as file:
while True:
chunk = file.read(1024 * 64)
if not chunk:
break
# handle the chunk
我尝试用和文本文件一样的写法:
def make_read(file, size):
def read():
return file.read(size)
return read
with open(path, 'rb') as file:
for chunk in iter(make_read(file, 1024 * 64), b''):
# handle the chunk
在Python中,遍历二进制文件的这种写法是对的吗?
5 个回答
13
在Python中,逐步读取二进制文件的一个很好的方法是使用内置的 iter
函数,配合两个参数,以及标准库中的 functools.partial
,具体可以参考Python文档。
iter
(对象[, 哨兵])这个函数会返回一个迭代器对象。第一个参数的含义会根据第二个参数是否存在而有所不同。如果没有第二个参数,对象必须是一个支持迭代的集合(也就是有
__iter__()
方法的对象),或者是一个支持序列协议的对象(也就是可以用整数从0
开始索引的对象)。如果不满足这两种情况,就会报TypeError
错误。如果提供了第二个参数 哨兵,那么 对象 必须是一个可调用的对象。在这种情况下,创建的迭代器会在每次调用__next__()
方法时调用 对象,如果返回的值等于 哨兵,就会抛出StopIteration
,否则就返回这个值。你也可以查看迭代器类型的相关内容。
使用
iter()
的第二种形式的一个实用例子是构建一个块读取器。例如,从一个二进制数据库文件中读取固定宽度的块,直到文件结束:from functools import partial with open('mydata.db', 'rb') as f: for block in iter(partial(f.read, 64), b''): process_block(block)
41
试试这个:
chunk_size = 4 * 1024 * 1024 # MB
with open('large_file.dat','rb') as f:
for chunk in iter(lambda: f.read(chunk_size), b''):
handle(chunk)
iter
需要一个没有参数的函数。
- 直接用
f.read
会把整个文件都读出来,因为没有指定size
参数; f.read(1024)
这行代码的意思是调用一个函数,并把它返回的值(从文件中加载的数据)传给iter
,所以iter
根本没有得到一个函数;(lambda:f.read(1234))
是一个不带参数的函数(在lambda
和:
之间没有东西),它会调用f.read(1234)
。
25
我不知道有没有现成的方法可以做到这一点,不过写一个包装函数其实很简单:
def read_in_chunks(infile, chunk_size=1024*64):
while True:
chunk = infile.read(chunk_size)
if chunk:
yield chunk
else:
# The chunk was empty, which means we're at the end
# of the file
return
然后在交互式提示符下:
>>> from chunks import read_in_chunks
>>> infile = open('quicklisp.lisp')
>>> for chunk in read_in_chunks(infile):
... print chunk
...
<contents of quicklisp.lisp in chunks>
当然,你也可以很容易地把这个改成用 with 语句块:
with open('quicklisp.lisp') as infile:
for chunk in read_in_chunks(infile):
print chunk
而且你可以像这样去掉 if 语句。
def read_in_chunks(infile, chunk_size=1024*64):
chunk = infile.read(chunk_size)
while chunk:
yield chunk
chunk = infile.read(chunk_size)