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


当前回答

如果你正在寻找一些快速的方法,这里有一个我一直在使用的方法,它已经工作了很多年:

from array import array

with open( path, 'rb' ) as file:
    data = array( 'B', file.read() ) # buffer the file

# evaluate it's data
for byte in data:
    v = byte # int value
    c = chr(byte)

如果你想迭代字符而不是整数,你可以简单地使用data = file.read(),它应该是py3中的bytes()对象。

其他回答

Python >= 3.8

多亏了walrus操作符(:=),解决方案非常短。我们从文件中读取bytes对象,并将它们赋值给变量byte

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

Python >= 3

在较旧的Python 3版本中,我们必须使用更详细的方式:

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

或者像benhoyt说的,跳过不相等,利用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)

Python >= 2.5

在Python 2中,这有点不同。这里我们得到的不是bytes对象,而是原始字符:

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

注意,with语句在Python 2.5以下版本中不可用。要在v 2.5中使用它,你需要导入它:

from __future__ import with_statement

在2.6中不需要这样做。

Python 2.4及更早版本

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

对于大尺寸,我认为使用生成器不会不好,这个答案是为了读取像文件这样的东西,虽然@codeapp有一个类似的答案,我认为删除内部循环会更有意义。

def read_chunk(file_object, chunk_size=125):
    while True:
        file =  file_object.read(chunk_size)
        if not file:
            break
        yield file


#sample use 
buffer = io.BytesIO()
file = open('myfile', 'r')
for chunk in read_chunk(file):
    buffer.write(chunk)
buffer.seek(0)
// save the file or do whatever you want here

你仍然可以使用它作为一个正常的列表,我不认为这是任何用途,但是

file_list = list(read_chunk(file, chunk_size=10000))
for i in file_list:
    # do something

然后得到每个数据块的索引

for index, chunk in enumurate(read_chunk(file, chunk_size=10000)):
    #use the index as a number index
    # you can try and get the size of each chunk with this 
    length = len(chunk)

注意,要注意文件的大小,chunk_size总是以字节为单位。

如果你正在寻找一些快速的方法,这里有一个我一直在使用的方法,它已经工作了很多年:

from array import array

with open( path, 'rb' ) as file:
    data = array( 'B', file.read() ) # buffer the file

# evaluate it's data
for byte in data:
    v = byte # int value
    c = chr(byte)

如果你想迭代字符而不是整数,你可以简单地使用data = file.read(),它应该是py3中的bytes()对象。

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

Python 3.5的新功能是pathlib模块,它有一个方便的方法,专门以字节的形式读取文件,允许我们遍历字节。我认为这是一个体面的答案(如果快速和肮脏):

import pathlib

for byte in pathlib.Path(path).read_bytes():
    print(byte)

有趣的是,这是提到pathlib的唯一答案。

在Python 2中,你可能会这样做(正如Vinay Sajip也建议的那样):

with open(path, 'b') as file:
    for byte in file.read():
        print(byte)

如果文件太大,无法在内存中遍历,则使用iter函数和可调用的哨兵签名(Python 2版本)对其进行分块:

with open(path, 'b') as file:
    callable = lambda: file.read(1024)
    sentinel = bytes() # or b''
    for chunk in iter(callable, sentinel): 
        for byte in chunk:
            print(byte)

(其他几个答案也提到了这一点,但很少提供合理的读取大小。)

用于大文件或缓冲/交互式读取的最佳实践

让我们创建一个函数来实现这一点,包括Python 3.5+标准库的惯用用法:

from pathlib import Path
from functools import partial
from io import DEFAULT_BUFFER_SIZE

def file_byte_iterator(path):
    """given a path, return an iterator over the file
    that lazily loads the file
    """
    path = Path(path)
    with path.open('rb') as file:
        reader = partial(file.read1, DEFAULT_BUFFER_SIZE)
        file_iterator = iter(reader, bytes())
        for chunk in file_iterator:
            yield from chunk

注意,我们使用file.read1。文件。read块,直到它得到它或EOF请求的所有字节。文件。Read1允许我们避免阻塞,因此它可以更快地返回。其他答案也没有提到这一点。

最佳实践使用的演示:

让我们创建一个带有兆字节(实际上是mebibyte)伪随机数据的文件:

import random
import pathlib
path = 'pseudorandom_bytes'
pathobj = pathlib.Path(path)

pathobj.write_bytes(
  bytes(random.randint(0, 255) for _ in range(2**20)))

现在让我们遍历它并在内存中物化它:

>>> l = list(file_byte_iterator(path))
>>> len(l)
1048576

我们可以检查数据的任何部分,例如,最后100字节和前100字节:

>>> l[-100:]
[208, 5, 156, 186, 58, 107, 24, 12, 75, 15, 1, 252, 216, 183, 235, 6, 136, 50, 222, 218, 7, 65, 234, 129, 240, 195, 165, 215, 245, 201, 222, 95, 87, 71, 232, 235, 36, 224, 190, 185, 12, 40, 131, 54, 79, 93, 210, 6, 154, 184, 82, 222, 80, 141, 117, 110, 254, 82, 29, 166, 91, 42, 232, 72, 231, 235, 33, 180, 238, 29, 61, 250, 38, 86, 120, 38, 49, 141, 17, 190, 191, 107, 95, 223, 222, 162, 116, 153, 232, 85, 100, 97, 41, 61, 219, 233, 237, 55, 246, 181]
>>> l[:100]
[28, 172, 79, 126, 36, 99, 103, 191, 146, 225, 24, 48, 113, 187, 48, 185, 31, 142, 216, 187, 27, 146, 215, 61, 111, 218, 171, 4, 160, 250, 110, 51, 128, 106, 3, 10, 116, 123, 128, 31, 73, 152, 58, 49, 184, 223, 17, 176, 166, 195, 6, 35, 206, 206, 39, 231, 89, 249, 21, 112, 168, 4, 88, 169, 215, 132, 255, 168, 129, 127, 60, 252, 244, 160, 80, 155, 246, 147, 234, 227, 157, 137, 101, 84, 115, 103, 77, 44, 84, 134, 140, 77, 224, 176, 242, 254, 171, 115, 193, 29]

对于二进制文件,不要逐行迭代

不要做下面的操作——这将拖动任意大小的块,直到它变成一个换行符——当块太小时速度太慢,而且可能也太大了:

    with open(path, 'rb') as file:
        for chunk in file: # text newline iteration - not for bytes
            yield from chunk

以上只适用于语义上人类可读的文本文件(如纯文本、代码、标记、markdown等)。基本上任何ascii, utf,拉丁语等…编码),您应该打开没有'b'标志。

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

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及更高版本中可用。