在Python中,如何读取二进制文件并在该文件的每一个字节上循环? html
Python 2.4及更早版本 python
f = open("myfile", "rb") try: byte = f.read(1) while byte != "": # Do stuff with byte. byte = f.read(1) finally: f.close()
Python 2.5-2.7 数组
with open("myfile", "rb") as f: byte = f.read(1) while byte != "": # Do stuff with byte. byte = f.read(1)
请注意,with语句在2.5如下的Python版本中不可用。 要在v 2.5中使用它,您须要导入它: 函数
from __future__ import with_statement
在2.6中是不须要的。 oop
Python 3 性能
在Python 3中,这有点不一样。 咱们将再也不以字节模式而是字节对象从流中获取原始字符,所以咱们须要更改条件: spa
with open("myfile", "rb") as f: byte = f.read(1) while byte != b"": # Do stuff with byte. byte = f.read(1)
或如benhoyt所说,跳过不等于并利用b""
评估为false的事实。 这使代码在2.6和3.x之间兼容,而无需进行任何更改。 若是从字节模式更改成文本模式或相反,也能够避免更改条件。 code
with open("myfile", "rb") as f: byte = f.read(1) while byte: # Do stuff with byte. byte = f.read(1)
若是文件不是太大,则将其保存在内存中是一个问题: htm
with open("filename", "rb") as f: bytes_read = f.read() for b in bytes_read: process_byte(b)
其中process_byte表示要对传入的字节执行的某些操做。 对象
若是要一次处理一个块:
with open("filename", "rb") as f: bytes_read = f.read(CHUNKSIZE) while bytes_read: for b in bytes_read: process_byte(b) bytes_read = f.read(CHUNKSIZE)
with
语句在Python 2.5及更高版本中可用。
该生成器从文件中产生字节,并分块读取文件:
def bytes_from_file(filename, chunksize=8192): with open(filename, "rb") as f: while True: chunk = f.read(chunksize) if chunk: for b in chunk: yield b else: break # example: for b in bytes_from_file('filename'): do_stuff_with(b)
总结chrispy,Skurmedel,Ben Hoyt和Peter Hansen的全部要点,这将是一次处理一个字节的二进制文件的最佳解决方案:
with open("myfile", "rb") as f: while True: byte = f.read(1) if not byte: break do_stuff_with(ord(byte))
对于python 2.6及更高版本,由于:
或使用JF Sebastians解决方案提升速度
from functools import partial with open(filename, 'rb') as file: for byte in iter(partial(file.read, 1), b''): # Do stuff with byte
或者,若是您但愿将其用做生成器功能(如codeape所示):
def bytes_from_file(filename): with open(filename, "rb") as f: while True: byte = f.read(1) if not byte: break yield(ord(byte)) # example: for b in bytes_from_file('filename'): do_stuff_with(b)
要读取文件(一次一个字节(忽略缓冲)),可使用内置两个参数的iter(callable, sentinel)
内置函数 :
with open(filename, 'rb') as file: for byte in iter(lambda: file.read(1), b''): # Do stuff with byte
它调用file.read(1)
直到不返回任何内容b''
(空字节串)。 对于大文件,内存不会无限增加。 您能够将buffering=0
传递给open()
,以禁用缓冲-它保证每次迭代仅读取一个字节(慢速)。
with
-statement会自动关闭文件-包括下面的代码引起异常的状况。
尽管默认状况下存在内部缓冲,可是每次处理一个字节仍然效率低下。 例如,这是blackhole.py
实用程序,可使用它所提供的全部内容:
#!/usr/bin/env python3 """Discard all input. `cat > /dev/null` analog.""" import sys from functools import partial from collections import deque chunksize = int(sys.argv[1]) if len(sys.argv) > 1 else (1 << 15) deque(iter(partial(sys.stdin.detach().read, chunksize), b''), maxlen=0)
例:
$ dd if=/dev/zero bs=1M count=1000 | python3 blackhole.py
它处理〜1.5 Gb / s的时chunksize == 32768
个人机器,只〜7.5 MB的/秒时chunksize == 1
。 也就是说,一次读取一个字节要慢200倍。 考虑到这一点,若是你能够重写你的处理同时使用多个字节, 若是你须要的性能。
mmap
容许您同时将文件视为bytearray
数组和文件对象。 若是您须要访问两个接口,它能够替代将整个文件加载到内存中的方法。 特别是,您可使用普通for
-loop一次遍历一个内存映射文件一个字节:
from mmap import ACCESS_READ, mmap with open(filename, 'rb', 0) as f, mmap(f.fileno(), 0, access=ACCESS_READ) as s: for byte in s: # length is equal to the current file size # Do stuff with byte
mmap
支持切片符号。 例如, mm[i:i+len]
从文件开始,从位置i
返回len
个字节。 Python 3.2以前不支持上下文管理器协议。 在这种状况下,您须要显式调用mm.close()
。 使用mmap
遍历每一个字节要比file.read(1)
消耗更多的内存,可是mmap
快一个数量级。