字节交换#
字节顺序和 ndarray 简介#
ndarray 是一个对象,它为内存中的数据提供了一个 python 数组接口.
通常发生的情况是,您想要使用数组查看的内存与运行 Python 的计算机的字节顺序不同.
例如,我可能正在使用具有小端 CPU(例如 Intel Pentium)的计算机,但是我从由大端计算机编写的文件中加载了一些数据.假设我从 Sun (big-endian) 计算机编写的文件中加载了 4 个字节.我知道这 4 个字节代表两个 16 位整数.在大端机器上,一个双字节整数首先存储最高有效字节(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 位和大端:
>>> import numpy as np
>>> big_end_arr = np.ndarray(shape=(2,),dtype='>i2', buffer=big_end_buffer)
>>> big_end_arr[0]
np.int16(1)
>>> big_end_arr[1]
np.int16(770)
请注意上面 dtype 的数组为 >i2 . > 表示“大端”( < 是小端), i2 表示“有符号 2 字节整数”.例如,如果我们的数据表示单个无符号 4 字节小端整数,则 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 - 在这种情况下,我们的底层数据是大端(数据字节序),并且我们已将 dtype 设置为匹配(dtype 也是大端).但是,有时您需要将这些翻转过来.
警告
标量不包含字节顺序信息,因此从数组中提取标量将返回本机字节顺序的整数.因此:
>>> 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()的作用.
您需要更改字节顺序的常见情况是:
您的数据和 dtype 字节序不匹配,并且您想要更改 dtype 以使其与数据匹配.
您的数据和 dtype 字节序不匹配,并且您想要交换数据以使其与 dtype 匹配
您的数据和dtype字节序匹配,但您希望交换数据并且dtype反映这一点
数据和dtype字节序不匹配,更改dtype以匹配数据#
我们制造一些不匹配的东西:
>>> wrong_end_dtype_arr = np.ndarray(shape=(2,),dtype='<i2', buffer=big_end_buffer)
>>> wrong_end_dtype_arr[0]
np.int16(256)
这种情况的显而易见的修复方法是更改dtype,使其给出正确的字节序:
>>> fixed_end_dtype_arr = wrong_end_dtype_arr.view(np.dtype('<i2').newbyteorder())
>>> fixed_end_dtype_arr[0]
np.int16(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]
np.int16(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]
np.int16(1)
>>> swapped_end_arr.tobytes() == big_end_buffer
False
使用ndarray的astype方法可以更容易地将数据强制转换为特定的dtype和字节顺序:
>>> swapped_end_arr = big_end_arr.astype('<i2')
>>> swapped_end_arr[0]
np.int16(1)
>>> swapped_end_arr.tobytes() == big_end_buffer
False