На этом шаге мы рассмотрим использование файлового метода readinto().
Вы хотите читать бинарные данные непосредственно в изменяемый буфер без какого-либо промежуточного копирования. Возможно, вы хотите изменить данные на месте и записать их обратно в файл.
Чтобы прочесть данные в изменяемый массив, используйте файловый метод readinto(). Например:
>>> import os.path >>> def read_into_buffer(filename): buf = bytearray(os.path.getsize(filename)) with open(filename, 'rb') as f: f.readinto(buf) return buf >>>
Вот пример использования:
>>> # Записываем файл примера >>> with open('sample.bin', 'wb') as f: f.write(b'Hello World') 11 >>> buf = read_into_buffer('sample.bin') >>> buf bytearray(b'Hello World') >>> buf[0:5] = b'Hallo' >>> buf bytearray(b'Hallo World') >>> with open('newsample.bin', 'wb') as f: f.write(buf) 11 >>>
Метод readinto() может быть использован для заполнения данными любого предварительно выделенного (preallocated) массива. Это даже включает массивы, созданные с помощью модуля array или библиотек типа numpy. В отличие от обычного метода read(), метод readinto() заполняет содержание текущего буфера вместо выделения и возвращения новых объектов. Так что вы можете использовать его, чтобы избежать излишних выделений памяти. Например, если вы читаете бинарный файл, состоящий из записей одинакового размера, то можете написать такую программу:
record_size = 32 # Размер каждой записи (значение для подгонки) buf = bytearray(record_size) with open('somefile', 'rb') as f: while True: n = f.readinto(buf) if n < record_size: break # Используем содержимое буфера . . .
Еще одна интересная возможность - функция memoryview(), которая позволяет делать срезы zero-copy существующего буфера и даже менять его содержимое.
https://ru.wikipedia.org/wiki/Zero-copy.
Например:
>>> buf bytearray(b'Hallo World') >>> m1 = memoryview(buf) >>> m2 = m1[-5:] >>> m2 <memory at 0x000001F542939E80> >>> m2[:] = b'WORLD' >>> buf bytearray(b'Hallo WORLD') >>>
При использовании метода f.readinto() нужно соблюдать осторожность: вы должны всегда проверять его код возврата, который является количеством фактически прочтенных байтов.
Если число байтов меньше размера предоставленного буфера, это может указывать на повреждение данных (например, если вы ожидали, что будет прочитано точное количество байтов).
И последнее: посмотрите на другие функции типа "into" в различных библиотечных модулях (например, recv_into(), pack_into() и т. д.). Многие другие компоненты Python имеют поддержку прямого ввода-вывода и доступа к данным, которая может быть использована для заполнения или изменения содержания массивов и буферов.
На следующем шаге мы рассмотрим отображаемые в память бинарные файлы.