Чтение двоичного плоского файла и пропуск байтов - PullRequest
1 голос
/ 02 марта 2020

У меня есть двоичный файл, в котором данные разбиты на группы по 400 байт. Я хочу построить массив типа np.uint32 из байтов в позиции 304 в позицию 308. Однако я не могу найти метод, предоставленный NumPy, который позволяет мне выбирать, какие байты читать, только начальное смещение, как определено в numpy.fromfile.

Например, если мой файл содержит 1000 групп по 400 байт, мне нужен массив размером 1000, такой что:

arr[0] = bytes 304-308
arr[1] = bytes 704-708
...
arr[-1] = bytes 399904 - 399908

Есть ли метод NumPy, который позволил бы мне указать, какие байты читать из буфера?

1 Ответ

1 голос
/ 02 марта 2020

Другой способ перефразировать то, что вы ищете (слегка), - сказать, что вы хотите прочитать uint32 числа, начиная со смещения 304, с шагом 400 байт. np.fromfile не предоставляет аргумента для вставки пользовательских шагов (хотя, вероятно, так и должно быть). У вас есть несколько вариантов на будущее.

Самый простой способ - это, вероятно, загрузить весь файл и выбрать нужную колонку:

data = np.fromfile(filename, dtype=np.uint32)[304 // 4::400 // 4].copy()

Если вы хотите больше контролировать точное позиционирование байтов (например, если смещение или размер блока не кратны 4), вместо этого вы можете использовать структурированные массивы:

dt = np.dtype([('_1', 'u1', 304), ('data', 'u4'), ('_2', 'u1', 92)])
data = np.fromfile(filename, dtype=dt)['data'].copy()

Здесь _1 и _2 используются для сброса ненужные байты с разрешением 1 байт, а не 4.

Загрузка всего файла, как правило, будет намного быстрее, чем поиск между чтениями, поэтому эти подходы, вероятно, желательны для файлов, которые помещаются в память. Если это не так, вы можете использовать отображение памяти или полностью домашнее решение.

Карты памяти могут быть реализованы с помощью модуля Pythons mmap и помещены в ndarray с использованием параметра buffer, или вы можете использовать класс np.memmap, который сделает это за вас:

mm = np.memmap(filename, dtype=np.uint32, mode='r', offset=0, shape=(1000, 400 // 4))
data = np.array(mm[:, 304 // 4])
del mm

Использование необработанного mmap возможно более эффективен, потому что вы можете указать шаги и смещение, которые смотрят прямо на карту, пропуская все дополнительные данные. Это также лучше, потому что вы можете использовать смещение и шаги, которые не кратны размеру np.uint32:

with open(filename, 'rb') as f, mmap.mmap(f.fileno(), length=0, access=mmap.ACCESS_READ) as mm:
    data = np.ndarray(buffer=mm, dtype=np.uint32, offset=304, strides=400, shape=1000).copy()

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...