Spec-Zone.ru › NumPy 2.0

Перестановка байтов

Введение в порядок байтов и ndarrays

ndarray — это объект, предоставляющий интерфейс массива Python для данных в памяти.

Часто бывает так, что память, которую вы хотите просмотреть с помощью массива, имеет порядок байтов, отличающийся от порядка байтов компьютера, на котором вы запускаете Python.

Например, я могу работать на компьютере с процессором little-endian — например, Intel Pentium, но загрузил данные из файла, созданного компьютером с big-endian. Предположим, я загрузил 4 байта из файла, созданного компьютером Sun (big-endian). Я знаю, что эти 4 байта представляют два 16-битных целых числа. На машине big-endian целое число длиной два байта хранится с самым старшим байтом (MSB) первым, а затем — с самым младшим байтом (LSB). Таким образом, байты в порядке памяти:

  1. MSB целого числа 1
  2. LSB целого числа 1
  3. MSB целого числа 2
  4. LSB целого числа 2

Предположим, что два целых числа были фактически 1 и 770. Поскольку 770 = 256 * 3 + 2, 4 байта в памяти содержали бы соответственно: 0, 1, 3, 2. Загруженные из файла байты содержали бы эти значения:

>>> big_end_buffer = bytearray([0,1,3,2])
>>> big_end_buffer
bytearray(b'\x00\x01\x03\x02')

Мы можем захотеть использовать ndarray для доступа к этим целым числам. В этом случае мы можем создать массив вокруг этой памяти и сообщить numpy, что есть два целых числа, и что они имеют 16 бит и big-endian:

>>> import numpy as np
>>> big_end_arr = np.ndarray(shape=(2,),dtype='>i2', buffer=big_end_buffer)
>>> big_end_arr[0]
1
>>> big_end_arr[1]
770

Обратите внимание на массив dtype выше типа >i2. > означает ‘big-endian’ (< — little-endian), а i2 означает «целое число со знаком длиной 2 байта». Например, если наши данные представляют одно целое число длиной 4 байта, без знака и little-endian, строка dtype будет <u4.

На самом деле, давайте попробуем это?

>>> little_end_u4 = np.ndarray(shape=(1,),dtype='<u4', buffer=big_end_buffer)
>>> little_end_u4[0] == 1 * 256**1 + 3 * 256**2 + 2 * 256**3
True

Возвращаясь к нашему big_end_arr — в этом случае наши базовые данные имеют big-endian (порядок байтов данных), и мы установили dtype для соответствия (dtype также big-endian). Однако иногда вам нужно поменять их местами.

Предупреждение

Скаляры не включают информацию о порядке байтов, поэтому извлечение скаляра из массива вернет целое число в порядке байтов по умолчанию. Следовательно:

>>> big_end_arr[0].dtype.byteorder == little_end_u4[0].dtype.byteorder
True

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

Изменение порядка байтов

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

  • Изменить информацию о порядке байтов в dtype массива, чтобы интерпретировать базовые данные как имеющие другой порядок байтов. Это задача arr.view(arr.dtype.newbyteorder())
  • Изменить порядок байтов базовых данных, оставив интерпретацию dtype как было. Это то, что делает arr.byteswap().

Общие ситуации, в которых вам нужно изменить порядок байтов:

  1. Порядок байтов данных и dtype не совпадает, и вы хотите изменить dtype так, чтобы он соответствовал данным.
  2. Порядок байтов данных и dtype не совпадает, и вы хотите поменять местами данные так, чтобы они соответствовали dtype
  3. Порядок байтов данных и dtype совпадают, но вы хотите поменять местами данные и dtype, чтобы отразить это

Порядок байтов данных и dtype не совпадают, изменить dtype для соответствия данным

Создадим пример, где они не совпадают:

>>> wrong_end_dtype_arr = np.ndarray(shape=(2,),dtype='<i2', buffer=big_end_buffer)
>>> wrong_end_dtype_arr[0]
256

Очевидное решение в этой ситуации — изменить dtype так, чтобы он давал правильный порядок байтов:

>>> fixed_end_dtype_arr = wrong_end_dtype_arr.view(np.dtype('<i2').newbyteorder())
>>> fixed_end_dtype_arr[0]
1

Обратите внимание, что массив в памяти не изменился:

>>> fixed_end_dtype_arr.tobytes() == big_end_buffer
True

Порядок байтов данных и типа не совпадают, изменить данные для соответствия dtype

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

>>> fixed_end_mem_arr = wrong_end_dtype_arr.byteswap()
>>> fixed_end_mem_arr[0]
1

Теперь массив изменился в памяти:

>>> fixed_end_mem_arr.tobytes() == big_end_buffer
False

Порядок байтов данных и dtype совпадают, поменять местами данные и dtype

У вас может быть правильно указанный тип массива dtype, но вам нужен массив с обратным порядком байтов в памяти, и вы хотите, чтобы dtype соответствовал, чтобы значения массива имели смысл. В этом случае вы просто выполняете оба предыдущих действия:

>>> swapped_end_arr = big_end_arr.byteswap()
>>> swapped_end_arr = swapped_end_arr.view(swapped_end_arr.dtype.newbyteorder())
>>> swapped_end_arr[0]
1
>>> swapped_end_arr.tobytes() == big_end_buffer
False

Более простой способ преобразования данных в определенный dtype и порядок байтов можно получить с помощью метода ndarray astype:

>>> swapped_end_arr = big_end_arr.astype('<i2')
>>> swapped_end_arr[0]
1
>>> swapped_end_arr.tobytes() == big_end_buffer
False

© 2005–2024 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/2.0/user/byteswapping.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API