В Python, как мне прочитать в двоичном файле и перебрать каждый байт этого файла?
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()
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 недоступен в версиях Python ниже 2.5. Чтобы использовать его в версии 2.5, вам нужно его импортировать:
from __future__ import with_statement
В 2.6 это не нужно.
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)
Или, как говорит Бенхойт, пропустите неравное и воспользуйтесь тем, что 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 3.8
Отныне благодаря оператору: = приведенный выше код может быть написан более коротким способом.
with open("myfile", "rb") as f:
while (byte := f.read(1)):
# Do stuff with byte.
Этот генератор возвращает байты из файла, читая файл кусками:
Смотрите документацию по Python для получения информации об итераторах и генераторах .
источник
8192 Byte = 8 kB
(на самом деле это,KiB
но это не так широко известно). Значение является «полностью» случайным, но 8 кБ, по-видимому, является подходящим значением: не слишком много памяти тратится впустую, и все же не существует «слишком много» операций чтения, как в принятом ответе Скурмеделя ...for b in chunk:
цикл наyield from chunk
. Эта формаyield
была добавлена в Python 3.3 (см. Выражения доходности ).Если файл не слишком большой, то проблема заключается в том, чтобы удерживать его в памяти:
где process_byte представляет некоторую операцию, которую вы хотите выполнить над переданным байтом.
Если вы хотите обрабатывать чанк одновременно:
with
Отчет доступен в Python 2.5 и выше.источник
Для чтения файла - по одному байту за раз (без учета буферизации) - вы можете использовать встроенную функцию с двумя аргументами
iter(callable, sentinel)
:Он вызывает,
file.read(1)
пока ничего не возвращаетb''
(пустая строка). Память не увеличивается неограниченно для больших файлов. Вы можете перейтиbuffering=0
кopen()
, чтобы отключить буферизацию - это гарантирует, что только один байт читается за итерацию (медленно).with
-statement автоматически закрывает файл - в том числе и в том случае, если приведенный ниже код вызывает исключение.Несмотря на наличие внутренней буферизации по умолчанию, по-прежнему неэффективно обрабатывать по одному байту за раз. Например, вот
blackhole.py
утилита, которая съедает все, что ей дают:Пример:
Он обрабатывает ~ 1,5 ГБ / с, когда
chunksize == 32768
на моей машине, и только ~ 7,5 МБ / с, когдаchunksize == 1
. То есть, он читает в 200 раз по одному байту за раз. Примите это во внимание, если вы можете переписать свою обработку, чтобы использовать более одного байта за раз, и если вам нужна производительность.mmap
позволяет обрабатывать файл как объектbytearray
и файл одновременно. Он может служить альтернативой загрузке всего файла в память, если вам нужен доступ к обоим интерфейсам. В частности, вы можете перебирать один байт за раз по отображенному в памяти файлу, просто используя простойfor
-loop:mmap
поддерживает обозначение среза. Например,mm[i:i+len]
возвращаетlen
байты из файла, начиная с позицииi
. Протокол менеджера контекста не поддерживается до Python 3.2; вам нужноmm.close()
явно позвонить в этом случае. Повторение каждого байта с использованиемmmap
потребляет больше памяти, чемfile.read(1)
, ноmmap
на порядок быстрее.источник
numpy
(байтовых) массивов с отображением в памяти.numpy.memmap()
и вы можете получать данные по одному байту за раз (ctypes.data). Вы можете думать о пустых массивах как о чем-то большем, чем о каплях в памяти + метаданных.Новым в Python 3.5 является
pathlib
модуль, который имеет удобный метод для чтения в файле как байты, что позволяет нам перебирать байты. Я считаю, что это достойный (если быстрый и грязный) ответ:Интересно, что это единственный ответ для упоминания
pathlib
.В Python 2 вы, вероятно, сделали бы это (как предлагает и Vinay Sajip):
В случае, если файл может быть слишком большим для итерации по памяти, вы бы идиотически разбили его на части, используя
iter
функцию сcallable, sentinel
сигнатурой - версию Python 2:(Несколько других ответов упоминают это, но немногие предлагают разумный размер чтения.)
Лучшая практика для больших файлов или буферизованного / интерактивного чтения
Давайте создадим для этого функцию, в том числе идиоматическое использование стандартной библиотеки для Python 3.5+:
Обратите внимание, что мы используем
file.read1
.file.read
блокирует, пока не получит все запрошенные байты илиEOF
.file.read1
позволяет нам избежать блокировки, и это может вернуться быстрее из-за этого. Никакие другие ответы не упоминают это также.Демонстрация использования лучших практик:
Давайте создадим файл с мегабайтом (на самом деле мегибайт) псевдослучайных данных:
Теперь давайте переберем его и осуществим в памяти:
Мы можем проверить любую часть данных, например, последние 100 и первые 100 байтов:
Не повторяйте строки для двоичных файлов
Не делайте следующее - это тянет кусок произвольного размера, пока не дойдет до символа новой строки - слишком медленный, когда чанки слишком малы и, возможно, слишком велики:
Вышесказанное подходит только для того, что является семантически читаемыми текстовыми файлами (такими как простой текст, код, разметка, разметка и т. Д., По сути, что угодно, в кодировке ascii, utf, latin и т. Д.), Которые следует открывать без
'b'
флага.источник
path = Path(path), with path.open('rb') as file:
вместо того, чтобы вместо этого использовать встроенную функцию открытия? Они оба делают одно и то же правильно?Path
объект, потому что это очень удобный новый способ обработки путей. Вместо того, чтобы передавать строку в тщательно выбранные «правильные» функции, мы можем просто вызывать методы объекта path, который, по сути, содержит большую часть важной функциональности, которую вы хотите, с семантически строкой пути. С IDE, которые могут проверять, мы можем легче получить автозаполнение. Мы могли бы сделать то же самое с помощьюopen
встроенной функции, но при написании программы у программиста есть множество положительных сторон для использования этогоPath
объекта.file_byte_iterator
намного быстрее, чем все методы, которые я пробовал на этой странице. Слава тебе!Подводя итог всем блестящим моментам Крисси, Скурмеделя, Бена Хойта и Питера Хансена, это было бы оптимальным решением для обработки двоичного файла по одному байту за раз:
Для версий Python 2.6 и выше, потому что:
Или используйте решение JF Sebastians для улучшения скорости
Или, если вы хотите, чтобы это была функция генератора, как продемонстрировано codeape:
источник
Python 3, прочитайте все файлы сразу:
Вы можете перебирать все что угодно, используя
data
переменную.источник
Попробовав все вышеперечисленное и воспользовавшись ответом @Aaron Hall, я получил ошибки памяти для файла размером ~ 90 МБ на компьютере под управлением Windows 10, 8 ГБ ОЗУ и 32-разрядной версии Python 3.5.
numpy
Вместо этого мне рекомендовал коллега, и он творит чудеса.Безусловно, самым быстрым для чтения всего двоичного файла (который я тестировал) является:
Ссылка
Множество людей быстрее, чем любые другие методы. Надеюсь, это поможет кому-то!
источник
numpy
, тогда может быть целесообразно.Если вам нужно прочитать много двоичных данных, вы можете рассмотреть модуль struct . Это задокументировано как преобразование «между типами C и Python», но, конечно, байты являются байтами, и не имеет значения, были ли они созданы как типы C. Например, если ваши двоичные данные содержат два 2-байтовых целых и одно 4-байтовое целое, вы можете прочитать их следующим образом (пример взят из
struct
документации):Это может оказаться более удобным, быстрым или иным, чем явным циклическим перемещением по содержимому файла.
источник
Этот пост сам по себе не является прямым ответом на вопрос. Вместо этого это расширяемый эталон на основе данных, который можно использовать для сравнения многих ответов (и вариантов использования новых функций, добавленных в более поздние, более современные версии Python), которые были опубликованы на этот вопрос - и поэтому должны быть полезным в определении того, у кого лучшая производительность.
В некоторых случаях я модифицировал код в указанном ответе, чтобы сделать его совместимым с платформой тестирования.
Во-первых, вот результаты последних версий Python 2 и 3:
Я также запустил его с гораздо большим тестовым файлом 10 МБ (который потребовался почти час) и получил результаты производительности, которые были сопоставимы с показанными выше.
Вот код, используемый для сравнительного анализа:
источник
yield from chunk
вместо этогоfor byte in chunk: yield byte
? Я думаю, что я должен ужесточить свой ответ с этим.yield from
.enumerate
поскольку итерацию следует понимать как завершенную - если нет, то, что я проверял последний раз - у enumerate есть некоторые накладные расходы с затратами по сравнению с ведением бухгалтерского учета для индекса с + = 1, так что вы могли бы альтернативно вести учет в своем собственный код Или даже перейти в deque сmaxlen=0
.enumerate
. Спасибо за ответ. Буду добавлять обновление в мой пост, в котором его нет (хотя я не думаю, что это сильно изменит результаты). Также будет добавлен ответ наnumpy
основе @Rick M.super().
вместоtuple.
своего,__new__
вы можете использоватьnamedtuple
имена атрибутов вместо индексов.если вы ищете что-то быстрое, вот метод, который я использовал, который работал годами:
если вы хотите перебирать символы вместо целых, вы можете просто использовать
data = file.read()
, который должен быть объектом bytes () в py3.источник