字节序交换#
字节序和 ndarray 简介#
ndarray
是一个为内存中的数据提供 Python 数组接口的对象。
通常情况下,您希望通过数组查看的内存与运行 Python 的计算机的字节序不同。
例如,我可能正在一台使用小端 CPU 的计算机(如 Intel Pentium)上工作,但我从一台大端计算机写入的文件中加载了一些数据。假设我从一台 Sun(大端)计算机写入的文件中加载了 4 个字节。我知道这 4 个字节代表两个 16 位整数。在大端机器上,一个两字节整数会先存储最高有效字节 (MSB),然后是最低有效字节 (LSB)。因此,这些字节在内存中的顺序是
整数 1 的 MSB
整数 1 的 LSB
整数 2 的 MSB
整数 2 的 LSB
假设这两个整数实际上是 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