Вопрос-Ответ

Reading binary file and looping over each byte [duplicate]

Чтение двоичного файла и перебор каждого байта

В Python, как я могу читать в двоичном файле и перебирать каждый байт этого файла?

Переведено автоматически
Ответ 1

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)

Или, как говорит бенхойт, пропустите значение not equal и воспользуйтесь тем фактом, что b"" вычисляется как false . Это делает код совместимым между 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. Чтобы использовать его в версии 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()
Ответ 2

Этот генератор выдает байты из файла, считывая файл порциями:

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 для получения информации о итераторах и генераторах.

Ответ 3

Если файл не слишком большой, то хранение его в памяти является проблемой:

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 и выше.

Ответ 4

Чтение двоичного файла в Python и перебор каждого байта


Новым в Python 3.5 является pathlib модуль, который имеет удобный метод, специально предназначенный для чтения в файле в виде байтов, позволяющий нам перебирать байты. Я считаю это достойным (хотя и быстрым и грязным) ответом.:

import pathlib

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

Интересно, что это единственный ответ, который стоит упомянуть pathlib.

В Python 2 вы, вероятно, сделали бы это (как также предлагает Винай Саджип):

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

В случае, если файл может быть слишком большим для итерации в памяти, вы должны разбить его на части идиоматически, используя iter функцию с callable, sentinel сигнатурой - версия 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. file.read блокирует до тех пор, пока не получит все запрошенные у него байты или EOF. file.read1 позволяет нам избежать блокировки, и из-за этого он может возвращаться быстрее. В других ответах это также не упоминается.

Демонстрация наилучшей практики использования:

Давайте создадим файл с мегабайтом (фактически мебибайтом) псевдослучайных данных:

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, latin и т.д. ), которые вы должны открывать без 'b' флага.

python