Перестановка байтов
Введение в порядок байтов и ndarrays
The ndarray — это объект, предоставляющий интерфейс массивов Python для данных в памяти.
Часто бывает, что память, которую вы хотите просмотреть с помощью массива, имеет порядок байтов, не совпадающий с порядком байтов компьютера, на котором вы выполняете Python.
Например, я могу работать на компьютере с процессором little-endian — таком как Intel Pentium, но я загрузил данные из файла, созданного компьютером с big-endian. Предположим, что я загрузил 4 байта из файла, созданного компьютером Sun (big-endian). Я знаю, что эти 4 байта представляют два 16-битных целых числа. На компьютере с big-endian двухбайтовое целое число хранится с самым старшим байтом (MSB) первым, а затем — с самым младшим байтом (LSB). Таким образом, байты в порядке памяти:
- MSB целое число 1
- LSB целое число 1
- MSB целое число 2
- 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 (порядок байтов данных), и мы установили тип данных, чтобы он соответствовал (тип данных также big-endian). Однако иногда вам нужно изменить это.
Предупреждение
Скаляры в настоящее время не включают информацию о порядке байтов, поэтому извлечение скаляра из массива вернёт целое число в родном порядке байтов. Следовательно:
>>> big_end_arr[0].dtype.byteorder == little_end_u4[0].dtype.byteorder True
Изменение порядка байтов
Как вы можете себе представить из введения, есть два способа повлиять на взаимосвязь между порядком байтов массива и лежащей в его основе памятью:
- Изменить информацию о порядке байтов в типе данных массива, чтобы он интерпретировал данные в памяти как данные с другим порядком байтов. Это роль
arr.newbyteorder() - Изменить порядок байтов в исходных данных, оставив интерпретацию типа данных как есть. Это то, что делает
arr.byteswap().
Общие ситуации, в которых вам нужно изменить порядок байтов:
- Порядок байтов ваших данных и типа данных не совпадает, и вы хотите изменить тип данных, чтобы он соответствовал данным.
- Порядок байтов ваших данных и типа данных не совпадает, и вы хотите поменять байты, чтобы они соответствовали типу данных.
- Порядок байтов ваших данных и типа данных совпадает, но вы хотите поменять байты и тип данных, чтобы это отражалось.
Порядок байтов данных и типа данных не совпадает, измените тип данных, чтобы он соответствовал данным
Мы создадим ситуацию, где они не совпадают:
>>> wrong_end_dtype_arr = np.ndarray(shape=(2,),dtype='<i2', buffer=big_end_buffer) >>> wrong_end_dtype_arr[0] 256
Явным решением этой ситуации является изменение типа данных, чтобы он задавал правильный порядок байтов:
>>> fixed_end_dtype_arr = wrong_end_dtype_arr.newbyteorder() >>> fixed_end_dtype_arr[0] 1
Обратите внимание, что массив в памяти не изменился:
>>> fixed_end_dtype_arr.tobytes() == big_end_buffer True
Порядок байтов данных и типа не совпадает, измените данные, чтобы они соответствовали типу
Вы можете захотеть сделать это, если вам нужно, чтобы данные в памяти имели определенный порядок. Например, вы можете записывать память в файл, которому требуется определенный порядок байтов.
>>> fixed_end_mem_arr = wrong_end_dtype_arr.byteswap() >>> fixed_end_mem_arr[0] 1
Теперь массив изменился в памяти:
>>> fixed_end_mem_arr.tobytes() == big_end_buffer False
Порядок байтов данных и типа данных совпадает, поменяйте данные и тип
У вас может быть правильно заданный тип данных массива, но вам нужен массив с противоположным порядком байтов в памяти, и вы хотите, чтобы тип данных соответствовал, чтобы значения массива имели смысл. В этом случае вы просто выполните обе предыдущие операции:
>>> swapped_end_arr = big_end_arr.byteswap().newbyteorder() >>> swapped_end_arr[0] 1 >>> swapped_end_arr.tobytes() == big_end_buffer False
Более простой способ преобразования данных к определенному типу данных и порядку байтов можно получить с помощью метода ndarray astype:
>>> swapped_end_arr = big_end_arr.astype('<i2')
>>> swapped_end_arr[0]
1
>>> swapped_end_arr.tobytes() == big_end_buffer
False
© 2005–2021 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.20/user/basics.byteswapping.html