读取二进制文件并循环每个字节

在Python中,如何读取二进制文件并循环该文件的每个字节?

f = open("myfile", "rb") try: byte = f.read(1) while byte != "": # Do stuff with byte. byte = f.read(1) finally: f.close() 

通过chrispy的build议:

 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这是不需要的。

在Python 3中,它有点不同。 我们将不再从字节模式的字节对象中获取原始字符,因此我们需要改变条件:

 with open("myfile", "rb") as f: byte = f.read(1) while byte != b"": # Do stuff with byte. byte = f.read(1) 

或者如本霍伊特所说,跳过不平等,并利用b""评估为假的事实。 这使代码兼容2.6和3.x没有任何改变。 如果你从字节模式转到文本模式,或者相反,它也可以避免你改变条件。

 with open("myfile", "rb") as f: byte = f.read(1) while byte: # Do stuff with byte. byte = f.read(1) 

该生成器从文件中产生字节,以块为单位读取文件:

 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) 

有关迭代器和生成器的信息,请参阅Python文档。

如果文件不是太大,把它放在内存中是一个问题:

 bytes_read = open("filename", "rb").read() for b in bytes_read: process_byte(b) 

其中process_byte表示您要对传入的字节执行的某个操作。

如果你想一次处理一个块:

 file = open("filename", "rb") try: bytes_read = file.read(CHUNKSIZE) while bytes_read: for b in bytes_read: process_byte(b) bytes_read = file.read(CHUNKSIZE) finally: file.close() 

要读取一个文件 – 一次一个字节(忽略缓冲) – 可以使用双参数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=0open() ,禁用缓冲 – 它保证每个迭代只读一个字节(慢)。

with -statement自动closures文件 – 包括下面的代码引发exception的情况。

尽pipe默认存在内部缓冲,但一次处理一个字节的效率仍然很低。 例如,以下是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 

当我的机器上chunksize == 32768时,它处理chunksize == 32768 GB / s ,当chunksize == 1时只处理chunksize == 1 MB / s 。 也就是说,一次读取一个字节的速度要慢200倍。 考虑到这一点,如果你可以重写你的处理,一次使用多个字节,并且你需要性能。

mmap允许你把一个文件同时作为一个bytearray和一个文件对象。 如果你需要访问这两个接口,它可以作为加载整个文件的替代scheme。 特别是,你可以在一个内存映射文件中一次迭代一个字节,只需要使用一个普通的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之前不支持上下文pipe理器协议; 你需要在这种情况下明确地调用mm.close() 。 迭代使用mmap每个字节比file.read(1)消耗更多的内存,但是mmap的速度要快一个数量级。

总结一下chrispy,Skurmedel,Ben Hoyt和Peter Hansen的所有优秀点,这将是一次处理一个二进制文件的最佳解决scheme:

 with open("myfile", "rb") as f: while True: byte = f.read(1) if not byte: break do_stuff_with(ord(byte)) 

对于Python版本2.6及以上,因为:

  • python在内部缓冲 – 不需要读取块
  • 干燥原则 – 不要重复读取线
  • 与声明确保一个干净的文件closures
  • 当没有更多字节时(不是字节为零时),“字节”计算为假

或者使用JF Sebastians解决scheme来提高速度

 from functools import partial with open(filename, 'rb') as file: for byte in iter(partial(file.read, 1), b''): # Do stuff with byte 

或者如果你想要它作为一个发电机function,如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) 

在Python中读取二进制文件并遍历每个字节

我们来创build一个函数来做到这一点:

 def file_byte_iterator(path): """given a path, return an iterator over the file that lazily loads the file """ with open(path, 'rb') as file: for chunk in file: for byte in chunk: yield byte 

例:

让我们来创build一个文件:

 >>> path = '/temp/foobarbaz' >>> with open(path, 'w') as f: ... f.write('foo\nbar\nbaz') 

现在让我们使用rb标志(读取模式,字节模式)进行迭代。

请注意,多个for循环不会增加复杂性(仍然是O(n)) – 这就是您如何懒惰地逐行遍历文件的方式。

这将循环在代码中的每个字节,没有任何hacky .read(1)业务。

 >>> for byte in file_byte_iterator(path): ... print(byte) ... #print(ord(byte)) # Python 2 102 111 111 10 98 97 114 10 98 97 122 

这是比我在其他答案中看到的while循环和复杂性更加Pythonic和自然。

缓冲读书

如果你有没有换行符的大文件,你可能想缓冲你的阅读。 Python 2.7需要io.open才能得到这个:

 >>> import io >>> with io.open('foobarbaz', 'rb') as f: ... for line in f: ... for byte in line: ... print(ord(byte)) 

我们现在有一个缓冲的阅读器:

 >>> f <_io.BufferedReader name='foobarbaz'> 

Python 3的内置open函数是2的io.open

如果你有很多二进制数据要读取,你可能要考虑结构模块 。 它被logging为“在C和Pythontypes之间转换”,但是当然,字节是字节,并且它们是否被创build为Ctypes并不重要。 例如,如果你的二进制数据包含两个2字节整数和一个4字节整数,你可以按如下方式读取它们(例子来自struct文档):

 >>> struct.unpack('hhl', b'\x00\x01\x00\x02\x00\x00\x00\x03') (1, 2, 3) 

您可能会发现这比使用文件内容显式循环更方便,更快速,或者两者兼而有之。

Python 3,立刻读取所有的文件:

 with open("filename", "rb") as binary_file: # Read the whole file at once data = binary_file.read() print(data) 

你可以使用datavariables迭代任何你想要的。

我的解决scheme实际上返回一个字符

 >>> from StringIO import StringIO >>> from functools import partial # credit to JF Sebastian >>> fl = StringIO('string\x00string2\x00') # simulated "file" object >>> bytearray(iter(partial(fl.read, 1), b'\x00')).__str__() # read until terminated 'string' >>> bytearray(iter(partial(fl.read, 1), b'\x00')).__str__() # again 'string2' 

唯一的问题是如果你正在pipe理一个外部偏移量,你需要明确地加1来弥补终止字符。

编辑:
通常虽然我会做一些像array('B',[ord(c) for c in file.read()]) ,并从那里手动进行数据pipe理。
(这是处理文件数据的最快方法,file.read(1)慢了很多倍)