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

Как в Python читать двоичный файл и перебирать каждый байт этого файла?


Python 2.4 и более ранние версии

  f = open ("myfile", "rb") try: byte = f.read (1) while byte! = "": # Что-то делать с байтом  .  byte = f.read (1) finally: f.close ()  

Python 2.5–2.7

   с open ("myfile", "rb") as f: byte = f.read (1) while byte! = "": # Что-то делать с байтом.  byte = f.read (1)  

Обратите внимание, что оператор with недоступен в версиях Python ниже 2.5. Чтобы использовать его в версии 2.5, вам необходимо импортировать его:

  из __future__ import with_statement  

В 2.6 это не необходимо.

Python 3

В Python 3 все несколько иначе. Мы больше не будем получать необработанные символы из потока в байтовом режиме, а будем получать байтовые объекты, поэтому нам нужно изменить условие:

  на open ("myfile", "rb")  as f: byte = f.read (1) while byte! = b "": # Что-то делать с байтом.  byte = f.read (1)  

Или, как говорит Бенхойт, пропустите не равно и воспользуйтесь тем, что b "" оценивает к ложному. Это делает код совместимым между 2.6 и 3.x без каких-либо изменений. Это также избавит вас от изменения условия, если вы перейдете из байтового режима в текстовый или наоборот.

  с open ("myfile", "rb") as f: byte  = f.read (1) while byte: # Что-то делать с байтом.  byte = f.read (1)  

python 3.8

С этого момента благодаря оператору: = оператор приведенный выше код может быть записан более коротким способом.

  с open ("myfile", "rb") как f: while (byte: = f.read (1))  : # Что-то делать с байтом.  

177

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

  def bytes_from_file (filename, chunksize = 8192): with open (filename, "rb"  ) как f: while True: chunk = f.read (chunksize) if chunk: for b в chunk: yield b else: break # пример: для b в bytes_from_file ('filename'): do_stuff_with (b)  

Информацию об итераторах и генераторах см. в документации Python.

Поделиться
Улучшить этот ответ
отредактировано 17 янв., В 23:31
ответил 23 июня ’09 в 21:50
  • 3
    @codeape Именно то, что я ищу. Но как определить размер фрагмента? Может быть это произвольное значение? — swdev 22 авг., 2014, 21:08
  • 3
    @swdev: в примере используется размер блока 8192 байта . Параметр для функции file.read () просто указывает размер, то есть количество байтов, которые нужно прочитать. codeape выбрал 8192 Byte = 8 kB (на самом деле это KiB , но это не так широко известно). Значение является «полностью» случайным, но 8 кБ кажется подходящим значением: не слишком много памяти тратится впустую и при этом не выполняется «слишком много» операций чтения, как в принятом ответе Скурмеделя … — mozzbozz 15 октября 2014 г. в 13:26
  • 4
    Файловая система уже буферизует блоки данных, поэтому этот код избыточен. Лучше читать по байтам. — Stark 20 нояб., В 15:39
  • 19
    Хотя это уже быстрее, чем принятый ответ, это можно ускорить еще на 20-25%, заменив весь внутренний цикл для b в chunk: на выход из чанка . Эта форма yield была добавлена ​​в Python 3.3 (см. Выражения доходности ). — Мартино, 14 фев. 2016, в 19:05
  • 3
    Для меня это медленнее, чем принятый ответ. Понятия не имею почему. — étale-cohomology 26 фев 2016, в 11:32
| показать 7 дополнительных комментариев

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

  def bytes_from_file (filename, chunksize = 8192): с open (filename, "rb") как f: while True: chunk = f.read (chunksize) if chunk: for  b в chunk: yield b else: break # пример: для b в bytes_from_file ('filename'): do_stuff_with (b)  

Информацию об итераторах и генераторах см. в документации Python.


55

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

  с open ("filename", "rb") as f: bytes_read = f.read () для b в bytes_read: process_byte  (b)  

где process_byte представляет некоторую операцию, которую вы хотите выполнить с переданным байтом.

Если вы хотите обработать фрагмент в время:

  с open ("filename", "rb") как f: byte  s_read = f.read (CHUNKSIZE) в то время как bytes_read: для b в bytes_read: process_byte (b) bytes_read = f.read (CHUNKSIZE)  

с оператор code> доступен в Python 2.5 и выше.

Поделиться
Улучшите это ответ
изменён 23 ноя ’19 в 22: 18
ответил 23 июня ’09 в 21:43
  • 1
    Возможно, вас заинтересует тест, который я только что опубликовал. — martineau 24 нояб. ’19 в 1:18
добавить комментарий |

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

  с open ("filename", "rb") как f: bytes_read = f.read () для b в bytes_read: process_byte (b)  

, где process_byte представляет некоторую операцию вы хотите выполнить с переданным байтом.

Если вы хотите обрабатывать фрагмент за раз:

  с open ("filename  "," rb ") как f: bytes_read = f.read (CHUNKSIZE), а bytes_read: для b в bytes_read: process_byte (b) bytes_read = f.read (CHUNKSIZE)  

Оператор with доступен в Python 2.5 и выше.


39

Чтобы прочитать файл — по одному байту (игнорируя буферизацию) — вы можете использовать двухаргументный iter (вызываемый, дозорный) встроенная функция:

  с open (filename, 'rb') как файл: для байта в iter (лямбда:  file.read (1), b ''): # Что-то делать с байтом  

Он вызывает file.read (1) , пока не вернет ничего b '' (пустая строка байтов). Для больших файлов объем памяти не увеличивается. Вы можете передать buffering = 0 в open () , чтобы отключить буферизацию — это гарантирует, что за итерацию будет прочитан только один байт (медленно).

with -statement автоматически закрывает файл, включая тот случай, когда приведенный ниже код вызывает исключение.

Несмотря на наличие внутренней буферизации по умолчанию обработка одного байта за раз по-прежнему неэффективна. Например, вот утилита blackhole.py , которая ест все, что ей дают:

  #!/Usr/bin/env python3 ""  "Отменить весь ввод.` Cat>/dev/null` аналог. "" "Import sysfrom functools import partialfrom collections import dequechunksize = int (sys.argv [1]) if len (sys.argv)> 1 else (1  

Пример:

  $ dd if =/dev/zero bs = 1M count = 1000 |  python3 blackhole.py  

Он обрабатывает ~ 1,5 ГБ/с , когда chunksize == 32768 на моем компьютере и только ~ 7. 5 МБ/с при chunksize == 1 . То есть читать по одному байту в 200 раз медленнее. Примите это во внимание, если вы можете переписать свою обработку, чтобы использовать более одного байта за раз, и , если вам нужна производительность.

mmap позволяет обрабатывать файл одновременно как массив байтов и объект файла. Он может служить альтернативой загрузке всего файла в память, если вам нужен доступ к обоим интерфейсам. В частности, вы можете выполнять итерацию по одному байту в файле с отображением памяти, просто используя простой for -loop:

  из mmap  импортировать ACCESS_READ, mmap с open (filename, 'rb', 0) как f, mmap (f.fileno (), 0, access = ACCESS_READ) как s: для байтов в s: # длина равна текущему размеру файла # Do  материал с байтом  

mmap поддерживает нотацию фрагментов. Например, mm [i: i + len] возвращает len байтов из файла, начиная с позиции i . Протокол диспетчера контекста не поддерживается до Python 3.2; в этом случае вам нужно явно вызвать mm.close () . Итерация по каждому байту с использованием mmap потребляет больше памяти, чем file.read (1) , но mmap — это порядок величина быстрее.

Поделиться
Улучшите этот ответ
отредактировал 19 июля ’17 в 7:12
ответил 16 ноя 2013, в 16:47
  • Последний пример показался мне очень интересным. Жаль, что нет эквивалентных массивов с отображением в память (байтов) numpy . — Мартино 04 дек. ’18 в 12:16
  • 2
    @martineau есть numpy.memmap () , и вы можете получать данные по одному байту (ctypes.data). Вы можете думать о массивах numpy как о немного большем, чем капли в памяти + метаданные. — jfs 04 дек. ’18 в 17:05
  • jfs: Спасибо, отличные новости! Не знал, что такое существует. Отличный ответ, кстати. — Мартино 4 дек. ’18 в 19:08
добавить комментарий |

Чтобы прочитать файл — по одному байту (игнорируя буферизацию) — вы можете использовать двухаргументный iter (callable, sentinel) встроенная функция:

  с open (filename, 'rb') as file: для байта в iter (lambda: file.  read (1), b ''): # Что-то делать с байтом  

Он вызывает file.read (1) , пока ничего не вернет b '' (пустая строка байтов). Память не увеличивается без ограничений для больших файлов. Вы можете передать buffering = 0 в open () , чтобы отключить буферизацию — это гарантирует, что за итерацию будет прочитан только один байт (медленно).

with -statement автоматически закрывает файл, включая тот случай, когда приведенный ниже код вызывает исключение.

Несмотря на наличие внутренней буферизации по умолчанию обработка одного байта за раз по-прежнему неэффективна. Например, вот утилита blackhole.py , которая ест все, что ей дают:

  #!/Usr/bin/env python3 ""  "Отменить весь ввод.` Cat>/dev/null` аналог. "" "Import sysfrom functools import partialfrom collections import dequechunksize = int (sys.argv [1]) if len (sys.argv)> 1 else (1  

Пример:

  $ dd if =/dev/zero bs = 1M count = 1000 |  python3 blackhole.py  

Он обрабатывает ~ 1,5 ГБ/с , когда chunksize == 32768 на моем компьютере и только ~ 7,5 МБ/с при chunksize == 1 . То есть читать по одному байту в 200 раз медленнее. Примите это во внимание, если вы можете переписать свою обработку, чтобы использовать более одного байта за раз, и , если вам нужна производительность.

mmap позволяет обрабатывать файл одновременно как массив байтов и объект файла. Он может служить альтернативой загрузке всего файла в память, если вам нужен доступ к обоим интерфейсам. В частности, вы можете выполнять итерацию по одному байту в файле с отображением памяти, просто используя простой for -loop:

  из mmap  импортировать ACCESS_READ, mmap с open (filename, 'rb', 0) как f, mmap (f.fileno (), 0, access = ACCESS_READ) как s: для байтов в s: # длина равна текущему размеру файла # Do  материал с байтом  

mmap поддерживает нотацию фрагментов. Например, mm [i: i + len] возвращает len байтов из файла, начиная с позиции i . Протокол диспетчера контекста не поддерживается до Python 3.2; в этом случае вам нужно явно вызвать mm.close () . Итерация по каждому байту с использованием mmap потребляет больше памяти, чем file.read (1) , но mmap — это порядок величина быстрее.


27

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

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

  импортировать pathlib для байта в pathlib.Path (path). read_bytes (): print (byte)  

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

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

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

В случае, если файл может быть слишком большим для перебора в памяти, вы бы разбили его идиоматически, используя iter с подписью callable, sentinel — версия Python 2:

  с open (path, 'b')  as file: callable = lambda: file.read (1024) sentinel = bytes () # или b '' для фрагмента в iter (вызываемого, sentinel): для байта в фрагменте: print (byte)  

(Об этом упоминается в нескольких других ответах, но немногие из них предлагают разумный размер чтения.)

Лучшая практика для больших файлов или буферизованного/интерактивного чтения

Давайте создайте функцию для этого, включая идиоматическое использование стандартной библиотеки для Python 3.5+:

  из p  athlib import Pathfrom functools import partialfrom io import DEFAULT_BUFFER_SIZEdef file_byte_iterator (path): "" "с указанием пути, вернуть итератор над файлом, который лениво загружает файл" "" path = Path (путь) с path.open ('rb')  как file: reader = partial (file.read1, DEFAULT_BUFFER_SIZE) file_iterator = iter (reader, bytes ()) для фрагмента в file_iterator: yield from chunk  

Обратите внимание, что мы используем file.read1 . file.read блокируется до тех пор, пока не будут получены все запрошенные байты или EOF . file.read1 позволяет нам избежать блокировки, и благодаря этому он может возвращаться быстрее. Никакие другие ответы не упоминают об этом.

Демонстрация передового опыта использования:

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

  import randomimport pathlibpath = 'pseudorandom_bytes'pathobj = pathlib.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, 17  6, 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]  

Не выполнять итерацию по строкам для двоичных файлов

Не делайте следующего — это извлекает кусок произвольного size, пока не дойдет до символа новой строки — слишком медленно, когда фрагменты слишком малы, и, возможно, также слишком велики:

  с open (path, 'rb') as file  : для фрагмента в файле: # итерация новой строки текста - не для выхода байтов из фрагмента  

Приведенное выше подходит только для семантически удобочитаемых текстовых файлов (например, обычного текста, кода, разметка, уценка и т. д. … практически все, что угодно в кодировке ascii, utf, latin и т. д. …), которую вы должны открывать без флага 'b' .

Поделиться
Улучшить этот ответ
отредактировано 24 ноября ’19 в 2:14
ответил 14 мая ’16 в 4: 16
  • 2
    Это НАСТОЛЬКО лучше … спасибо за это. Я знаю, что не всегда весело возвращаться к ответу двухлетней давности, но я ценю, что вы это сделали. Мне особенно нравится подзаголовок «Не повторять по строкам» 🙂 — Флорис, 9 апр. 2018, 16:18
  • 1
    Привет, Аарон, есть ли причина, по которой вы решили использовать path = Path (путь) с путем. open ('rb') as file: вместо использования встроенной функции open? Они оба делают одно и то же, верно? — Джошуа Йонатан, 02 янв., В 17:51
  • 1
    @JoshuaYonathan Я использую объект Path , потому что это очень удобный новый способ обработки путей. Вместо того, чтобы передавать строку тщательно выбранным «правильным» функциям, мы можем просто вызвать методы объекта пути, который, по сути, содержит большинство важных функций, которые вам нужны, с тем, что семантически является строкой пути. С помощью IDE, которые могут проверять, нам также легче получить автозаполнение. Мы могли бы сделать то же самое с помощью встроенной функции open , но при написании программы программист может использовать вместо него объект Path . — Аарон Холл ♦ 02 янв., 12:51
  • 1
    Последний метод, который вы упомянули с использованием функции, file_byte_iterator , намного быстрее, чем все методы, которые я пробовал на этой странице. Престижность вам! — Рик М. 14 марта ’19 в 13:02
  • @RickM: Возможно, вас заинтересует тест, который я только что опубликовал. — martineau 24 нояб. ’19 в 1:20
добавить комментарий |

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

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

  импортировать pathlib для байта в pathlib.Path (path) .read_bytes (): print (byte)   

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

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

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

В случае, если файл может быть слишком большим для перебора в памяти, вы бы разбили его идиоматически, используя функцию iter с вызываемая, дозорная подпись — версия Python 2:

  с open (path, 'b') as file: callable = lambda: file.read (1024  ) sentinel = bytes () # или b '' для фрагмента в iter (callable, sentinel): для байта в фрагменте: print (byte)  

(Это упоминается в нескольких других ответах, но немногие предлагают разумный размер чтения.)

Лучшая практика для больших файлов или буферизованного/интерактивного чтения

Давайте создадим есть функция для этого, включая идиоматическое использование стандартной библиотеки для Python 3. 5+:

  from pathlib import Pathfrom functools import partialfrom io import DEFAULT_BUFFER_SIZEdef file_byte_iterator (path): "" "с указанием пути, вернуть итератор над файлом, который лениво загружает файл  "" "path = путь (путь) с path.open ('rb') как file: reader = partial (file.read1, DEFAULT_BUFFER_SIZE) file_iterator = iter (reader, bytes ()) для фрагмента в file_iterator: yield from chunk  

Обратите внимание, что мы используем file.read1 . file.read блокируется до тех пор, пока не будут получены все запрошенные байты или EOF . file.read1 позволяет нам избежать блокировки, и благодаря этому он может возвращаться быстрее. Никакие другие ответы не упоминают об этом.

Демонстрация передового опыта использования:

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

  import randomimport pathlibpath = '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, 22  3, 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]  

Не выполнять итерацию по строкам для двоичных файлов

Не делайте следующего: это вытягивает кусок произвольного размера, пока не дойдет до символа новой строки — слишком медленно, когда фрагменты слишком малы, а возможно, и слишком велики:

  с open (path, 'rb') as file: for chunk in file: # text newline iteration - not for  выход байтов из чанка  

Вышеупомянутое подходит только для того, что семантически читается человеком. ext файлы (например, простой текст, код, разметка, уценка и т. д. … практически все, что угодно в кодировке ascii, utf, латинского и т. д. …), которые вы должны открывать без флага 'b' .


19

Подводя итог всем блестящим моментам chrispy, Skurmedel, Ben Hoyt и Peter Hansen, это было бы оптимальным решением для обработки двоичного файла по одному байту за раз:

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

Для версий Python 2.6 и выше, потому что :

  • буферы python внутри — нет необходимости читать чанки
  • принцип DRY — не повторять строку чтения
  • with обеспечивает чистое закрытие файла.
  • ‘byte’ принимает значение false, когда байтов больше нет (не когда байт равен нулю)

Или используйте решение JF Sebastians для повышения скорости

  из functools import partialwith open (filename, 'rb') as file: for byte in iter (partial (file.read, 1)  , b ''): # Что-то делать с байтом  

Или я если вы хотите, чтобы это была функция генератора, как показано на 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)) # пример: для b в bytes_from_file ('filename'): do_stuff_with (b)  

Поделиться
Улучшить этот ответ
отредактировано 9 мая 2016 года в 8:16
ответ дан 06 сен. ’13 в 07:55
  • 2
    Как говорится в связанном ответе, чтение/обработка по одному байту в Python все еще выполняется медленно, даже если чтения буферизированы. Производительность можно значительно улучшить, если одновременно обрабатывать несколько байтов, как в примере в связанном ответе: 1,5 ГБ/с против 7,5 МБ/с. — jfs 9 мая 2016, 11:49
добавить комментарий |

Подводя итог всем блестящим точкам Chrispy, Skurmedel, Ben Hoyt и Peter Hansen, это было бы оптимальным решением для обработки двоичного файла файл по одному байту за раз:

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

Для версий python 2.6 и выше, потому что:

  • буферы python внутри — нет необходимости для чтения фрагментов
  • Принцип DRY — не повторять строку чтения
  • with оператор гарантирует чистый файл close
  • ‘byte’ оценивает значение false, когда байтов больше нет (не когда байт равен нулю)

Или используйте J. F. Решение Себастьяна для повышения скорости

  из functools import partialwith open (filename, 'rb') as file: for byte in iter (partial (file.read, 1), b '  '): # Делайте что-нибудь с байтом  

Или, если вы хотите, чтобы это была функция генератора, как показано на codeape:

  def  bytes_from_file (filename): с open (filename, "rb") as f: while True: byte = f.read (1) if not byte: break yield (ord (byte)) # пример: для b в bytes_from_file ('filename  '): do_stuff_with (b)  

8

Этот пост сам по себе не является прямым ответом на вопрос. Вместо этого это управляемый данными расширяемый эталонный тест, который можно использовать для сравнения многих ответов (и вариантов использования новых функций, добавленных в более поздних, более современных версиях Python), которые были опубликованы на этот вопрос — и поэтому должны быть полезным при определении того, какой из них имеет лучшую производительность.

В некоторых случаях я изменил код в указанном ответе, чтобы сделать его совместимым с платформой тестирования.

Во-первых, вот результаты последних версий Python 2 и 3:

  От самых быстрых до самых медленных скоростей выполнения с 32-битным Python 2.7.16 numpy версии 1.16  .5 Размер тестового файла: 1024 КиБ 100 выполнений, лучшее из 3 повторов 1 Tcll (array.array): 3,8943 секунды, скорость отн. 1,00x, на 0,00% медленнее (262,95 КБ/с) 2 Vinay Sajip (чтение всего в память): 4,1164  с, скорость отн. 1,06x, на 5,71% медленнее (248,76 КБ/с) 3 codeape + iter + partial: 4,1616 с, скорость отн. 1,07x, на 6,87% медленнее (246,06 КБ/с) 4 codeape: 4,1889 с, скорость откл.  d 1,08x, на 7,57% медленнее (244,46 КиБ/с) 5 Винай Саджип (по фрагментам): 4,1977 с, относительная скорость 1,08x, на 7,79% медленнее (243,94 КБ/с) 6 Аарон Холл (версия Py 2): 4,2417 с, отн.  скорость 1.09x, на 8.92% медленнее (241.41 KiB/sec) 7 gerrit (struct): 4.2561 sec, rel speed 1.09x, на 9.29% медленнее (240,59 KiB/sec) 8 Rick M. (numpy): 8.1398 sec, rel speed 2.09  x, на 109,02% медленнее (125,80 КиБ/с) 9 Skurmedel: 31,3264 с, скорость отн. 8,04x, на 704,42% медленнее (32,69 КБ/с) Время выполнения теста (мин: с) - 03:26  

  От самой быстрой до самой медленной скорости выполнения с 32-битным Python 3.8.0 numpy версии 1.17.4 Размер тестового файла: 1024 КиБ 100 выполнений, лучшее из 3 повторов1 Vinay Sajip + "  доходность от "+" моржового оператора ": 3,5235 с, скорость отн. 1,00x, на 0,00% медленнее (290,62 КБ/с) 2 Аарон Холл +" yield from ": 3,5284 с, скорость отн. 1,00x, на 0,14% медленнее (290,22 КБ/с)  сек) 3 codeape + iter + partial + «yield from»: 3,5303 секунды, скорость отн. 1,00x, на 0,19% медленнее (290,06 KiB/sec) 4 Vinay Sajip + «yield from»: 3,531  2 секунды, скорость отн. 1,00x, на 0,22% медленнее (289,99 КБ/с) 5 кодовая лента + «yield from» + «оператор моржа»: 3,5370 с, скорость отн. 1,00x, на 0,38% медленнее (289,51 КБ/с) 6 codeape +  "yield from": 3. 5390 секунд, скорость отн. 1,00x, на 0,44% медленнее (289,35 КБ/с) 7 jfs (mmap): 4,0612 с, скорость отн. 1,15x, на 15,26% медленнее (252,14 КиБ/с) 8 Vinay Sajip (чтение всего в память):  4,5948 с, скорость отн. 1,30x, на 30,40% медленнее (222,86 КБ/с) 9 codeape + iter + partial: 4,5994 с, скорость отн. 1,31x, на 30,54% медленнее (222,64 КБ/с) 10 кодовая лента: 4,5995 с, скорость отн. 1,31  x, на 30,54% медленнее (222,63 КиБ/с) 11 Винай Саджип (по фрагментам): 4,6110 с, относительная скорость 1,31x, на 30,87% медленнее (222,08 КБ/с) 12 Аарон Холл (версия Py 2): 4,6292 с, относительная скорость 1,31  x, на 31,38% медленнее (221,20 КиБ/с) 13 Tcll (array.array): 4,8627 с, скорость отн. 1,38x, на 38,01% медленнее (210,58 КБ/с) 14 gerrit (struct): 5,0816 с, скорость отн. 1,44x,  На 44,22% медленнее (201,51 КиБ/с) 15 Рик М. (numpy) + «yield from»: 11,8084 с, скорость отн. 3,35x, на 235,13% медленнее (86,72 КБ/с) 16 Скурмедель: 11,8806 с, скорость отн. 3,37x,  На 237,18% медленнее (86,19 КБ/с) 17 Rick M. (numpy): 13,3860 с, скорость отн. В 3,80 раза, на 279,91% медленнее (76,50 КБ/с)  ark runtime (min: sec) - 04:47  

Я также запустил его с гораздо большим тестовым файлом размером 10 МБ (запуск которого занял почти час) и получил результаты производительности, которые были сопоставимы с показанными выше.

Вот код, использованный для тестирования:

  from __future__ import print_functionimport arrayimport atexitfrom collection import deque, namedtupleimport  iofrom mmap import ACCESS_READ, mmapimport numpy as npfrom operator import attrgetterimport osimport randomimport Structimport sysimport tempfilefrom textwrap import dedentimport timeimport timeitimport tracebacktry: xrangeexcept NameError: # Python 3 xrange = rangeclass единиц в KiB (целое число) для количества единиц KiB (int)  информации.  "" "def __new __ (self, value = 0): return 1024 * valueBIG_TEST_FILE = 1 # MiBs или 0 для небольшого файла.SML_TEST_FILE = KiB (64) EXECUTIONS = 100 # Количество выполнений каждого" алгоритма "за период выполнения  .TIMINGS = 3 # Количество запусков тайминга .CHUNK_SIZE = KiB (8) if BIG_TEST_FILE: FILE_SIZE = KiB (1024) * BIG_TEST_FILEelse: FILE_SIZE = SML_TEST_FILE # Для более быстрого тестирования. # Общая настройка для всех алгоритмов - с префиксом для настройки каждого алгоритма.  COMMON_SETUP = dedent ("" "# Сделать доступным в алгоритмах. From __main__ import array, deque, get_buffer_size, mmap, np, struct from __main__ import ACCESS_READ, CHUNK_SIZE, FILE_SIZE, TEMP_FILENAME from functools Name, исключая Python Name import partial try: x  xrange = range "" ") def get_buffer_size (path):" "" Определяет оптимальный размер буфера для чтения файлов.  "" "st = os.stat (path) try: bufsize = st.st_blksize # Доступно в некоторых системах Unix (например, Linux), кроме AttributeError: bufsize = io.DEFAULT_BUFFER_SIZE return bufsize # Утилита, в первую очередь, для использования при встраивании дополнительных алгоритмов в тест.  VERIFY_NUM_READ = "" "# Проверить, что генератор считывает правильное количество байтов (предполагается, что значения верны). bytes_read = sum (1 для _ в file_byte_iterator (TEMP_FILENAME)) assert bytes_read == FILE_SIZE,  'Неверное количество сгенерированных байтов: получено {:,} вместо {:,}'. format (bytes_read, FILE_SIZE) "" "ВРЕМЯ  = namedtuple ('TIMING', 'label, exec_time') class Algorithm (namedtuple ('CodeFragments', 'setup, test')): # Фрагмент кода timeit "stmt" по умолчанию. _TEST = "" "# для b в file_byte_iterator (  TEMP_FILENAME): # Перебирать каждый байт.  # pass # Что-то делать с байтом ... deque (file_byte_iterator (TEMP_FILENAME), maxlen = 0) # Приемник данных.  "" "# Должен перегружать __new__, потому что (именованные) кортежи неизменяемы. Def __new __ (cls, setup, test = None):" "" Строковые аргументы фрагмента кода без изменений.  Аргументы: `setup` - фрагмент кода, который определяет вещи, используемые кодом` test`.  В этом случае он должен определить функцию генератора с именем file_byte_iterator (), которой будет передано это имя тестового файла двоичных данных.  Этот код не рассчитан.  `test` - Фрагмент кода, который использует вещи, определенные в коде` setup`.  По умолчанию _TEST.  Это код, рассчитанный по времени.  "" "test = cls._TEST if test is None else test # Используйте значение по умолчанию, если он не указан. # Раскомментируйте, чтобы заменить все тесты производительности тестом, который проверяет правильное # количество значений байтов, генерируемых функцией file_byte_iterator. #test  = VERIFY_NUM_READ вернуть кортеж .__ new __ (cls, (dedent (setup), dedent (test))) sizes = {'Aaron Hall (Py 2 version)': Algorithm ("" "def file_byte_iterator (path): with open (path,  "rb") как файл: callable = partial (file.read, 1024) sentinel = bytes () # или b '' для фрагмента в iter (callable, sentinel): для байта в фрагменте: yield byte "" "),"  codeape ": алгоритм (" "" def file_byte_iterator (filename, chunksize = CHUNK_SIZE): с open (filename, "rb") как f: while True: chunk = f.read (chunksize) if chunk: for b in chunk: yield  b else: break "" ")," codeape + iter + partial ": алгоритм (" "" def file_byte_iterator (filename, chunksize = CHUNK_SIZE): с open (filename, "rb") as f: for chunk in iter (частично  (f.read, chunksize), b ''): for b in chunk: yield b "" "),"  gerrit (struct) ": Algorithm (" "" def file_byte_iterator (filename): with open (filename, "rb") as f: fmt = '{} B'.format (FILE_SIZE) # Читает весь файл сразу.  для b в struct.unpack (fmt, f.read ()): yield b "" "), 'Rick M. (numpy)': алгоритм (" "" def file_byte_iterator (filename): для байта в np.fromfile (  filename, 'u1'): yield byte "" ")," Skurmedel ": Алгоритм (" "" def file_byte_iterator (filename): с open (filename, "rb") как f: byte = f.read (1) в то время как  byte: yield byte byte = f.read (1) "" ")," Tcll (массив. array) ": Algorithm (" "" def file_byte_iterator (filename): with open (filename, "rb") as f: arr = array.array ('B') arr.fromfile (f, FILE_SIZE) # Читает весь файл в  один раз. для b в arr: yield b "" ")," Vinay Sajip (прочитать все в память) ": алгоритм (" "" def file_byte_iterator (filename): с open (filename, "rb") as f: bytes_read =  f.read () # Читает сразу весь файл. for b в bytes_read: yield b "" ")," Vinay Sajip (chunked) ": Algorithm (" "" def file_byte_iterator (filename, chunksize = CHUNK_SIZE): with open (  filename, "rb") как f: chunk = f.read (chunksize) while chunk: for b in chunk: yield b chunk = f.read (chunksize) "" "),} # Завершить алгоритмы ## Версии алгоритмов, которые  будет работать только в определенных версиях (или более поздних) Python. # if sys.version_info> = (3, 3): алгоритмы.update ({'codeape + iter + partial + "yield from"': Algorithm ("" "def  file_byte_iterator (filename, chunksize = CHUNK_SIZE): с open (filename, "rb") as f: для фрагмента в iter (partial (f.read, chunksize), b ''): yield from chunk "" "), '  codeape + "yield from" ': алгоритм ("" "def file_byte_iterator (filename, chunksize = CHUNK_SIZE): с open (filename," rb ") как f: while True: chunk = f.read (chunksize) if chunk: yield  from chunk else: break "" ")," jfs (mmap) ": алгоритм (" "" def file_byte_iterator (filename): с open (filename, "rb") как f,  mmap (f.fileno (), 0  , access = ACCESS_READ) как s: yield from s "" "), 'Rick M. (numpy) +" yield from "': алгоритм (" "" def file_byte_iterator (filename): # data = np.fromfile (filename,  'u1') yield from np.fromfile (filename, 'u1') "" "), 'Vinay Sajip +" yield from "': алгоритм (" "" def file_byte_iterator (filename, chunksize = CHUNK_SIZE): with open (filename  , "rb") как f: chunk = f.read (chunksize) while chunk: yield from chunk # Добавлено в Py 3.3 chunk = f.read (chunksize) "" "),}) # Конец Python 3.3 update.if sys  .version_info> = (3, 5): алгоритмы.update ({'Aaron Hall + "yield from"': Algorithm ("" "from pathlib import Path def file_byte_iterator (path): '' 'По заданному пути, вернуть итератор)  над файлом  который лениво загружает файл.  '' 'path = Path (путь) bufsize = get_buffer_size (путь) с path.open (' rb ') as file: reader = partial (file.read1, bufsize) для фрагмента в iter (reader, bytes ()): yield  from chunk "" "),}) # Конец Python 3.5 update.if sys.version_info> = (3, 8, 0): algorithmms.update ({'Vinay Sajip +" yield from "+" оператор моржа "': алгоритм  ("" "def file_byte_iterator (filename, chunksize = CHUNK_SIZE): с open (filename," rb ") как f: while chunk: = f. read (chunksize): yield from chunk # Добавлено в Py 3.3 "" "), 'codeape +" yield from "+" оператор моржа "': алгоритм (" "" def file_byte_iterator (filename, chunksize = CHUNK_SIZE): с open (  filename, "rb") как f: while chunk: = f.read (chunksize): yield from chunk "" "),}) # Конец Python 3.8.0 update.update. #### Main #### def  main (): global TEMP_FILENAME def cleanup (): "" "Очистить после завершения тестирования.  "" "try: os.remove (TEMP_FILENAME) # Удалить временный файл. except Exception: pass atexit.register (cleanup) # Создать именованный временный двоичный файл псевдослучайных байтов для тестирования. fd, TEMP_FILENAME = tempfile.mkstemp (  '.bin') с os.fdopen (fd, 'wb') как файл: os.write (fd, bytearray (random.randrange (256) for _ in range (FILE_SIZE))) # Выполнить и время для каждого алгоритма, собрать  results. start_time = time.time () # Чтобы определить, сколько времени займет само тестирование. timings = [] для метки в алгоритмах: попробуйте: time = TIMING (label, min (timeit.repeat (алгоритмы [label] .test, setup =  COMMON_SETUP + алгоритмы [label] .setup, repeat = TIMINGS, number = EXECUTIONS))) за исключением Exception as exc: print ('{} произошло синхронизация алгоритма: "{}"  n {}'. Format (type (exc)  .__ name__, label, exc)) traceback.print_exc (file = sys.stdout) # Перенаправление на stdout. sys.exit (1) timings.append (time) # Отчет о результатах. print ('От самой быстрой до самой медленной скорости выполнения с {}  -bit Python {}. {}. {} '. format (64, если sys.maxsize> 2 ** 32 иначе 32, * sys  .version_info [: 3])) print ('numpy version {}'. format (np.version.full_version)) print ('Размер тестового файла: {:,} KiB'.format (FILE_SIZE//KiB (1))  ) print ('{:, d} выполнений, лучшее из {: d} повторов'.format (EXECUTIONS, TIMINGS)) print () longest = max (len (Timing.label) для таймингов) # Длина самого длинного идентификатора  .  ranked = sorted (timeings, key = attrgetter ('exec_time')) # Сортировка так, чтобы самая быстрая была первой.  fastest = ranked [0] .exec_time для ранга, время в enumerate (rank, 1): print ('{:  {width}}: {: 8.4f} secs, rel speed {: 6.2f}  x, {: 6.2f}% медленнее '({: 6.2f} КиБ/сек)'. формат (ранг, метка времени, метка времени. exec_time, раунд (тайминги. время  .exec_time/fastest - 1) * 100, 2), (FILE_SIZE/Timing.exec_time)/KiB (1), # в секунду ширина = самая длинная)) print () мин, сек = divmod (time.time () -  start_time, 60) print ('Время выполнения теста (мин: сек) - {: 02d}: {: 02d}'. format (int (mins), int (round (secs)))) main ()  

Поделиться
Улучшить этот ответ
отредактировано 23 января ’20 в 17:47
ответил 24 ноября ’19 в 13:12
  • Вы предполагаете, что я использую yield from chunk вместо для байта в chunk: yield byte код>? Думаю, на этом мне стоит закрепить свой ответ. - Аарон Холл ♦ 24 нояб., В 01:54
  • @Aaron: есть две версии вашего ответа в результатах Python 3, и в одной из них используется yield from . — Мартино 24 нояб. ’19 в 2:00
  • Хорошо, я обновил свой ответ. также я предлагаю вам отказаться от enumerate , поскольку итерация должна пониматься как завершенная — если нет, последний раз я проверял — у enumerate есть небольшие накладные расходы с расходами на ведение бухгалтерского учета для индекса с + = 1 , так что в качестве альтернативы вы можете вести учет в своем собственном коде. Или даже перейти к двухсторонней очереди с maxlen = 0 . — Аарон Холл ♦ 24 нояб., В 02:42
  • @Aaron: согласитесь с перечислением . Спасибо за ответ. Я буду добавлять обновление к моему сообщению, в котором его нет (хотя я не думаю, что это сильно изменит результаты). Также будет добавлен ответ @Rick M. на основе numpy . — Мартино 24 нояб. ’19 в 3:18
  • 1
    Еще немного обзора кода: я не думаю, что сейчас имеет смысл писать ответы на Python 2 — я бы подумал об удалении Python 2, как если бы ожидаем, что вы будете использовать 64-битный Python 3.7 или 3.8. Вы можете настроить очистку в конце с помощью atexit и частичного приложения. Опечатка: «проверить». Не вижу смысла в дублировании тестовых строк — они вообще разные? Я полагаю, если вы используете super (). вместо кортеж. в своем __ new__ , вы можете использовать namedtuple имена атрибутов вместо индексов. — Аарон Холл ♦ 24 нояб., В 3:37
| показать 2 дополнительных комментария

Сама по себе эта публикация не является прямым ответом на вопрос. Вместо этого это управляемый данными расширяемый эталонный тест, который можно использовать для сравнения многих ответов (и вариантов использования новых функций, добавленных в более поздних, более современных версиях Python), которые были опубликованы на этот вопрос — и поэтому должны быть полезным при определении того, какой из них имеет лучшую производительность.

В некоторых случаях я изменил код в указанном ответе, чтобы сделать его совместимым с платформой тестирования.

Во-первых, вот результаты последних версий Python 2 и 3:

  От самых быстрых до самых медленных скоростей выполнения с 32-битным Python 2.7.16 numpy версии 1.16  .5 Размер тестового файла: 1024 КиБ 100 выполнений, лучшее из 3 повторов 1 Tcll (array.array): 3,8943 секунды, скорость отн. 1,00x, на 0,00% медленнее (262,95 КБ/с) 2 Vinay Sajip (чтение всего в память): 4,1164  с, скорость отн. 1,06x, на 5,71% медленнее (248,76 КБ/с) 3 codeape + iter + partial: 4,1616 с, скорость отн. 1,07x, на 6,87% медленнее (246,06 КБ/с) 4 codeape: 4,1889 с, скорость откл.  d 1,08x, на 7,57% медленнее (244,46 КиБ/с) 5 Винай Саджип (по фрагментам): 4,1977 с, относительная скорость 1,08x, на 7,79% медленнее (243,94 КБ/с) 6 Аарон Холл (версия Py 2): 4,2417 с, отн.  скорость 1.09x, на 8.92% медленнее (241.41 KiB/sec) 7 gerrit (struct): 4.2561 sec, rel speed 1.09x, на 9.29% медленнее (240,59 KiB/sec) 8 Rick M. (numpy): 8.1398 sec, rel speed 2.09  x, на 109,02% медленнее (125,80 КиБ/с) 9 Skurmedel: 31,3264 с, скорость отн. 8,04x, на 704,42% медленнее (32,69 КБ/с) Время выполнения теста (мин: с) - 03:26  

  От самой быстрой до самой медленной скорости выполнения с 32-битным Python 3.8.0 numpy версии 1.17.4 Размер тестового файла: 1024 КиБ 100 выполнений, лучшее из 3 повторов1 Vinay Sajip + "  доходность от "+" моржового оператора ": 3,5235 с, скорость отн. 1,00x, на 0,00% медленнее (290,62 КБ/с) 2 Аарон Холл +" yield from ": 3,5284 с, скорость отн. 1,00x, на 0,14% медленнее (290,22 КБ/с)  сек) 3 codeape + iter + partial + «yield from»: 3,5303 секунды, скорость отн. 1,00x, на 0,19% медленнее (290,06 KiB/sec) 4 Vinay Sajip + «yield from»: 3,531  2 секунды, скорость отн. 1,00x, на 0,22% медленнее (289,99 КБ/с) 5 кодовая лента + «yield from» + «оператор моржа»: 3,5370 с, скорость отн. 1,00x, на 0,38% медленнее (289,51 КБ/с) 6 кодовая лента +  «yield from»: 3,5390 с, скорость отн. 1,00x, на 0,44% медленнее (289,35 КБ/с) 7 jfs (mmap): 4,0612 с, скорость отн. 1,15x, на 15,26% медленнее (252,14 КБ/с) 8 Vinay Sajip (читать  все в память): 4,5948 сек, скорость отн. 1,30x, на 30,40% медленнее (222,86 КиБ/с) 9 codeape + iter + partial: 4,5994 с, скорость отн. 1,31x, на 30,54% медленнее (222,64 КБ/с) 10 codeape: 4,5995  с, скорость отн. в 1,31 раза, на 30,54% медленнее (222,63 КБ/с) 11 Винай Саджип (по фрагментам): 4,6110 с, скорость отн. в 1,31 раза, на 30,87% медленнее (222,08 КБ/с) 12 Аарон Холл (версия Py 2): 4,6292  сек, скорость отн. 1,31x, на 31,38% медленнее (221,20 КБ/с) 13 Tcll (array.array): 4,8627 с, скорость отн. 1,38x, на 38,01% медленнее (210,58 КиБ/с) 14 геррит (структура): 5,0816 с,  скорость отн. 1,44x, на 44,22% медленнее (201. 51 KiB/sec) 15 Rick M. (numpy) + «yield from»: 11,8084 секунды, скорость отн. 3,35x, на 235,13% медленнее (86,72 KiB/с) 16 Skurmedel: 11,8806 с, скорость отн. 3,37x, на 237,18% медленнее (  86,19 КБ/с) 17 Рик М. (numpy): 13,3860 с, скорость отн. 3,80x, на 279,91% медленнее (76,50 КБ/с) Время выполнения теста (мин: с) - 04:47  

Я также запустил его с гораздо большим тестовым файлом 10 МиБ (запуск которого занял почти час) и получил результаты производительности, сопоставимые с показанными выше.

Вот используемый код для проведения сравнительного анализа:

  from __future__ import print_functionimport arrayimport atexitfrom collection import deque, namedtupleimport iofrom mmap import ACCESS_READ, mmapimport numpy as npfrom operator import attmpgetterimport osimport randomimport demoimport текстовый файл  timeimport timeitimport tracebacktry: xrangeexcept NameError: # Python 3 xrange = rangeclass KiB (int): "" "KibiBytes - количество байтовых единиц, кратное количеству  информации.  "" "def __new __ (self, value = 0): return 1024 * valueBIG_TEST_FILE = 1 # MiBs или 0 для небольшого файла.SML_TEST_FILE = KiB (64) EXECUTIONS = 100 # Количество выполнений каждого" алгоритма "за период выполнения  .TIMINGS = 3 # Количество запусков тайминга .CHUNK_SIZE = KiB (8) if BIG_TEST_FILE: FILE_SIZE = KiB (1024) * BIG_TEST_FILEelse: FILE_SIZE = SML_TEST_FILE # Для более быстрого тестирования. # Общая настройка для всех алгоритмов - с префиксом для настройки каждого алгоритма.  COMMON_SETUP = dedent ("" "# Сделать доступным в алгоритмах. From __main__ import array, deque, get_buffer_size, mmap, np, struct from __main__ import ACCESS_READ, CHUNK_SIZE, FILE_SIZE, TEMP_FILENAME from functools Name, исключая Python Name import partial try: x  xrange = range "" ") def get_buffer_size (path):" "" Определяет оптимальный размер буфера для чтения файлов.  "" "st = os.stat (path) try: bufsize = st.st_blksize # Доступно в некоторых системах Unix (например, Linux), кроме AttributeError: bufsize = io.DEFAULT_BUFFER_SIZE return bufsize # Утилита в первую очередь для использования при встраивании дополнительных алгоритмов в тест.  VERIFY_NUM_READ = "" "# Убедитесь, что генератор считывает правильное количество байтов (предполагается, что значения верны).  bytes_read = sum (1 для _ в file_byte_iterator (TEMP_FILENAME)) assert bytes_read == FILE_SIZE,  'Неверное количество сгенерированных байтов: получено {:,} вместо {:,}'. format (bytes_read, FILE_SIZE) "" "ВРЕМЯ  = namedtuple ('TIMING', 'label, exec_time') class Algorithm (namedtuple ('CodeFragments', 'setup, test')): # Фрагмент кода timeit "stmt" по умолчанию. _TEST = "" "# для b в file_byte_iterator (  TEMP_FILENAME): # Перебирать каждый байт.  # pass # Что-то делать с байтом ... deque (file_byte_iterator (TEMP_FILENAME), maxlen = 0) # Приемник данных.  "" "# Должен перегрузить __new__, потому что (именованные) кортежи неизменяемы. def __new __ (cls, setup, test = None): "" "Строковые аргументы фрагмента кода Dedent (undent). Args:` setup` - Фрагмент кода, который определяет вещи, используемые кодом `test`. В этом случае он должен определять  функция генератора с именем `file_byte_iterator ()`, которой будет передано это имя тестового файла с двоичными данными. Этот код не привязан по времени. `test` - Фрагмент кода, который использует вещи, определенные в коде` setup`. По умолчанию _TEST.  - это код, рассчитанный по времени. "" "test = cls._TEST if test is None else test # Используйте значение по умолчанию, если оно не указано.  # Раскомментируйте, чтобы заменить все тесты производительности тестом, который проверяет правильное # количество значений байтов, генерируемых функцией file_byte_iterator.  #test = VERIFY_NUM_READ возвращает кортеж .__ new __ (cls, (dedent (setup), dedent (test))) sizes = {'Aaron Hall (Py 2 version)': Algorithm ("" "def file_byte_iterator (path): with open (  путь, "rb") как файл: callable = partial (file.read, 1024) sentinel = bytes () # или b '' для фрагмента в iter (callable, sentinel): для байта в фрагменте: yield byte "" ")  , "codeape": алгоритм ("" "def file_byte_iterator (filename, chunksize = CHUNK_SIZE): с open (filename," rb ") как f: while True: chunk = f.read (chunksize) if chunk: для b в фрагменте  : yield b else: break "" ")," codeape + iter + partial ": алгоритм (" "" def file_byte_iterator (filename, chunksize = CHUNK_SIZE): с open (filename, "rb") as f: for chunk in iter  (partial (f.read, chunksize), b ''): для b в chunk: yield b "" ")," gerrit (struct) ": алгоритм (" "" def file_byte_iterator (filename): with open (filename,  "rb") as f: fmt = '{} B'.format (FILE_SIZE) # Читает сразу весь файл. for b в struct.unpack (fmt, f.read ()): yield b "" "),'  Рик М. (numpy) ': Алгоритм ("" "def  file_byte_iterator (filename): для байта в np.fromfile (filename, 'u1'): yield byte "" ")," Skurmedel ": алгоритм (" "" def file_byte_iterator (filename): с открытым (имя_файла, "rb")  как f: byte = f.read (1) while byte: yield byte byte = f.read (1) "" ")," Tcll (array.array) ": алгоритм (" "" def file_byte_iterator (filename): with  open (filename, "rb") as f: arr = array.array ('B') arr.fromfile (f, FILE_SIZE) # Читает файл целиком.  for b в arr: yield b "" ")," Vinay Sajip (прочитать все в память) ": алгоритм (" "" def file_byte_iterator (filename): с open (filename, "rb") как f: bytes_read = f.  read () # Читает сразу весь файл. for b в bytes_read: yield b "" ")," Vinay Sajip (chunked) ": Algorithm (" "" def file_byte_iterator (filename, chunksize = CHUNK_SIZE): with open (filename,  "rb") как f: chunk = f.read (chunksize), а chunk: for b в chunk: yield b chunk = f. read (chunksize) "" "),} # Завершить алгоритмы ## Версии алгоритмов, которые будут работать только в определенных (или более поздних) версиях Python. # if sys.version_info> = (3, 3): algorithmms.update ({  'codeape + iter + partial + "yield from"': алгоритм ("" "def file_byte_iterator (filename, chunksize = CHUNK_SIZE): с open (filename," rb ") как f: для фрагмента в iter (частичное (f.read  , chunksize), b ''): yield from chunk "" "), 'codeape +" yield from "': алгоритм (" "" def file_byte_iterator (filename, chunksize = CHUNK_SIZE): with open (filename, "rb")  as f: while True: chunk = f.read (chunksize) if chunk: yield from chunk else: break "" ")," jfs (mmap) ": алгоритм (" "" def file_byte_iterator (filename): with open (filename  , "rb") как f,  mmap (f.fileno (), 0, access = ACCESS_READ) как s: yield from s "" "), 'Rick M. (numpy) +" yield from "': Алгоритм (  "" "def file_byte_iterator (filename): # data = np.fromfile (filename, 'u1') yield from np.fromfile (filename, 'u1')" ""), 'Vinay Sajip + "yield from"': Алгоритм  ("" "def file_byte_iterato  r (filename, chunksize = CHUNK_SIZE): с open (filename, "rb") как f: chunk = f.read (chunksize), а chunk: yield from chunk # Добавлено в Py 3.3 chunk = f.read (chunksize) ""  "),}) # Конец Python 3.3 update.if sys.version_info> = (3, 5): алгоритмы.update ({'Aaron Hall +" yield from "': Algorithm (" "" from pathlib import Path def file_byte_iterator (  path): '' 'По заданному пути вернуть итератор по файлу, который лениво загружает файл.  '' 'path = Path (путь) bufsize = get_buffer_size (путь) с path.open (' rb ') as file: reader = partial (file.read1, bufsize) для фрагмента в iter (reader, bytes ()): yield  from chunk "" "),}) # Завершить Python 3.5 update.if sys.version_info> = (3, 8, 0): algorithmms.update ({'Vinay Sajip +" yield from "+" оператор моржа "': алгоритм  ("" "def file_byte_iterator (filename, chunksize = CHUNK_SIZE): open (filename," rb ") as f: while chunk: = f.read (chunksize): yield from chunk # Добавлено в Py 3.3" ""),  'codeape + "yield from" + "оператор моржа"': алгоритм ("" "def file_byte_iterator (filename, chunksize = CHUNK_SIZE): с open (filename," rb ") как f: while chunk: = f.read (chunksize  ): yield from chunk "" "),}) # Завершить Python 3.8.0 update.update. #### Main #### def main (): global TEMP_FILENAME def cleanup ():" "" Очистить после тестирования  выполнен.  "" "try: os.remove (TEMP_FILENAME) # Удалить временный файл. except Exception: pass atexit.register (cleanup) # Создать именованный временный двоичный файл псевдослучайных байтов для тестирования. fd, TEMP_FILENAME = tempfile.mkstemp (  '.bin') с ОС. fdopen (fd, 'wb') as file: os.write (fd, bytearray (random.randrange (256) for _ in range (FILE_SIZE))) # Выполнить и задать время для каждого алгоритма, собрать результаты.  start_time = time.time () # Чтобы определить, сколько времени займет само тестирование.  timings = [] для метки в алгоритмах: try: Timing = TIMING (метка, мин (timeit.repeat (алгоритмы [метка] .test, setup = COMMON_SETUP + алгоритмы [метка] .setup, repeat = TIMINGS, number = EXECUTIONS))  ) кроме Exception as exc: print ('{} произошло синхронизация алгоритма: "{}"  n {}'. format (type (exc) .__ name__, label, exc)) traceback.print_exc (file = sys.stdout)  # Перенаправить на стандартный вывод.  sys.exit (1) timings.append (time) # Отчет о результатах.  print ('От самой быстрой до самой медленной скорости выполнения с {} -битным Python {}. {}. {}'. format (64, если sys.maxsize> 2 ** 32, иначе 32, * sys.version_info [: 3])) print  ('numpy version {}'. format (np.version.full_version)) print ('Размер тестового файла: {:,} KiB'.format (FILE_SIZE//KiB (1))) print (' {:, d}  исполнений, лучшее из {: d} повторов'.format (EXECUTIONS, TIMINGS)) print () longest = max (len (Timing.label) для таймингов) # Длина самого длинного идентификатора.  ranked = sorted (timeings, key = attrgetter ('exec_time')) # Сортировка так, чтобы самая быстрая была первой.  fastest = ranked [0] .exec_time для ранга, время в enumerate (rank, 1): print ('{:  {width}}: {: 8.4f} secs, rel speed {: 6.2f}  x, {: 6.2f}% медленнее '({: 6.2f} КиБ/сек)'. формат (ранг, метка времени, метка времени. exec_time, round (time.exec_time/fastest, 2), round ((time  .exec_time/fastest - 1) * 100, 2), (FILE_SIZE/Timing.exec_time)/KiB (1), # в секунду ширина = самая длинная)) print () мин, сек = divmod (time.time () -  start_time, 60) print ('Время выполнения теста (мин: сек) - {: 02d}: {: 02d}'. format (int (mins), int (round (secs)))) main ()  

6

Python 3, прочитать весь файл сразу:

  с open ("filename", "rb") as binary_file: # Прочитать сразу весь файл data = binary_file.read () print (data)  

Вы можете перебирать все, что хотите, используя переменную data .

Поделиться
Улучшите этот ответ
отредактировано 25 сентября ’16 в 17:47
Arman H
4,88888 золотых знаков4646 серебряных знаков6969 бронзовых знаков
ответил 25 сен 2016, в 17:26
добавить комментарий |

Python 3, прочитать весь файл сразу:

  с open ("filename", "rb"  ) как двоичный_файл: # Прочитать сразу весь файл data = binary_file.read () print (data)  

Вы можете повторять все, что хотите, используя data переменная


6 iv>

Попробовав все вышеперечисленное и используя ответ от @Aaron Hall, я получал ошибки памяти для файла размером ~ 90 МБ на компьютере под управлением Windows 10, 8 ГБ ОЗУ и Python 3.5 32 -немного. Коллега порекомендовал мне использовать вместо него numpy , и это творит чудеса.

Безусловно, самый быстрый способ чтения всего двоичного файла (который я тестировал) это:

  импорт numpy как npfile = "binary_file.bin" data = np.fromfile (file, 'u1')  

Справка

На данный момент намного быстрее, чем любые другие методы. Надеюсь, это кому-то поможет!

Поделиться
Улучшите этот ответ
ответил 19 марта ’19 в 13:45
  • @Nirmal: Речь идет о зацикливании байта охвата, поэтому неясно, имеет ли ваш комментарий о различных типах данных какое-либо отношение. — Мартино 24 нояб. ’19 в 2:08
  • 1
    Рик: Ваш код не выполняет то же самое, что и другие, а именно перебирает каждый байт. Если это добавить к нему, он не будет быстрее, чем большинство других, по крайней мере, согласно результатам моего теста. На самом деле это один из самых медленных подходов. Если обработка каждого байта (что бы это ни было) могла быть выполнена с помощью numpy , тогда может быть полезно. — Мартино 24 нояб. ’19 в 22:24
  • @martineau Спасибо за ваши комментарии, да, я понимаю, что вопрос касается цикла по каждому байту, а не просто загрузки всего за один раз, но в этом вопросе есть другие ответы, которые также указывают на чтение всего содержимого и отсюда и мой ответ — Рик М. 25 нояб., в 11:54
  • @Nirmal Вы тоже ошибаетесь. numpy из файла может читать разные типы с помощью dtypes: ===================================== dtheader = np.dtype ([(‘Начальное имя’, ‘b’, (4,)), (‘Тип сообщения’, np.int32, (1,)), (‘Экземпляр’, np.int32, (1,)), ( ‘NumItems’, np.int32, (1,)), (‘Length’, np.int32, (1,)), (‘ComplexArray’, np.int32, (1,))]) dtheader = dtheader.newbyteorder (‘>’) headerinfo = np.fromfile (iqfile, dtype = dtheader, count = 1) — Курт Петерс, 19 июня ’20 в 21:13
  • @KurtPeters О, я не знал, что вы можете пройти пользовательский dtype. Благодаря! — Нирмал 20 июн ’20 в 16:24
добавить комментарий |

Попробовав все вышеперечисленное и используя ответ от @Aaron Hall, я получал ошибки памяти для файла размером ~ 90 МБ на компьютере. под управлением Window 10, 8 ГБ ОЗУ и 32-разрядной версии Python 3.5. Коллега порекомендовал мне использовать вместо него numpy , и это творит чудеса.

Безусловно, самый быстрый способ чтения всего двоичного файла (который я тестировал) это:

  импорт numpy как npfile = "binary_file.bin" data = np.fromfile (file, 'u1')  

Справка

На данный момент намного быстрее, чем любые другие методы. Надеюсь, это кому-то поможет!


4

Если вы есть много двоичных данных для чтения, вы можете рассмотреть модуль структуры. Он задокументирован как преобразование «между типами C и Python», но, конечно, байты являются байтами, и были ли они созданы как типы C, не имеет значения. Например, если ваши двоичные данные содержат два 2-байтовых целых числа и одно 4-байтовое целое число, вы можете прочитать их следующим образом (пример взят из документации struct ):

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

Возможно, вам будет удобнее, быстрее или и то, и другое, чем явный цикл по содержимому файла.

Поделиться
Улучшите это ответ
Создан 01 июл. в 14: 24
добавить комментарий |

Если у вас есть много двоичных данных для чтения, вы можете рассмотреть возможность использования модуля struct. Он задокументирован как преобразование «между типами C и Python», но, конечно, байты являются байтами, и были ли они созданы как типы C, не имеет значения. Например, если ваши двоичные данные содержат два 2-байтовых целых числа и одно 4-байтовое целое число, вы можете прочитать их следующим образом (пример взят из документации struct ):

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

Возможно, вам будет удобнее, быстрее или и то, и другое, чем явный цикл по содержимому файла.


3

Если вы ищете что-то быстрое, вот метод, который я использовал, который работал годами:

  из массива import array с open (path, 'rb') as file: data = array ('B', file.read ()) # буферизировать файл # оценить его данные для байта в data: v  = byte # int value c = chr (byte)  

, если вы хотите перебирать символы вместо целых чисел, вы можете просто использовать data = file.read () , который должен быть объектом bytes () в py3.

Поделиться
Улучшите это ответ
изменён 23 июля ’18 в 12:30
ответил 22 сентября 2015, 15:20
  • 1
    ‘массив’ импортируется ‘из массива импорта массива’ — quanly_mc 23 июля ’18 at 6 : 19
  • @quanly_mc да , спасибо, что уловили это, и извините, что я забыл включить это, редактирую сейчас. — Tcll 23 июля 2018 г., 12:30
добавить комментарий |

если вы ищете что-то быстрое, вот метод, который я использовал, который работал годами:

  из массива import array с open (path, 'rb') as file: data = array ('B', file.read ()) # буферизируем файл # оцениваем его данные для  byte in data: v = byte # int value c = chr (byte)  

если вы хотите перебирать символы вместо целых чисел, вы можете просто использовать data = file.read () , который должен быть объектом bytes () в py3.


0

Вот пример чтения данных с порядком байтов в сети с использованием Numpy fromfile с адресацией @Nirmal комментариев выше:

  dtheader = np.dtype ([('Start Name', '  b ', (4,)), (' Тип сообщения ', np.int32, (1,)), (' Экземпляр ', np.int32, (1,)), (' NumItems ', np.int32, (  1,)), ('Длина', np.int32, (1,)), ('ComplexArray', np.int32, (1,))]) dtheader = dtheader.newbyteorder ('>') headerinfo = np.  fromfile (iqfile, dtype = dtheader, count = 1) print (raw ['Start Name'])  

Надеюсь, это поможет. Проблема в том, что fromfile не распознает EOF и не позволяет корректно выйти из цикла для файлов произвольного размера.

Поделиться
Улучшить этот ответ
ответил 19 июня ’20 в 21:18
добавить комментарий |

Вот пример чтения сетевых данных с порядком байтов с использованием Numpy fromfile с адресацией комментариев @Nirmal выше:

  dtheader = np.dtype ([('Начальное имя', 'b', (4,)), ('Тип сообщения', np.int32, (1,)), ('Экземпляр', np.int32  , (1,)), ('NumItems', np.int32, (1,)), ('Длина', np.int32, (1,)), ('ComplexArray', np.int32, (1,)  )]) dtheader = dtheader.newbyteorder ('>') headerinfo = np.fromfile (iqfile, dtype = dtheader, count = 1) print (raw ['Start Name'])  

Надеюсь, это поможет. Проблема в том, что fromfile не распознает EOF и позволяет корректно выйти из цикла для файлов произвольного размера.

Оцените статью
Botgadget.ru
Добавить комментарий