Byte-swapping

Introduction to byte ordering and ndarrays

ndarray是一个对象,它为内存中的数据提供了一个python数组接口。

它经常发生,你想用数组查看的内存不是与运行Python的计算机相同的字节顺序。

例如,我可能在一个带有小端CPU的计算机上工作 - 例如Intel Pentium,但是我已经从大计算机写入的文件加载了一些数据。假设我从一个Sun(big-endian)计算机写入的文件中加载了4个字节。我知道这4个字节表示两个16位整数。在大端机器上,以最高有效字节(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_str = chr(0) + chr(1) + chr(3) + chr(2)
>>> big_end_str
'\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_str)
>>> big_end_arr[0]
1
>>> big_end_arr[1]
770

请注意dtype上的数据类型>i2>意味着“big-endian”(<是小端字节序),而i2意味着“有符号2字节整数”。例如,如果我们的数据表示单个无符号的4字节小端整数,则dtype字符串将是<u4

其实,为什么我们不试试呢?

>>> little_end_u4 = np.ndarray(shape=(1,),dtype='<u4', buffer=big_end_str)
>>> 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

Changing byte ordering

正如你可以想象的从介绍,有两种方式可以影响数组的字节顺序和它正在查看的底层内存之间的关系:

  • 更改数组dtype中的字节排序信息,以便将不合法的数据解释为不同的字节顺序。这是arr.newbyteorder()的作用
  • 更改基础数据的字节顺序,保留dtype解释。这是arr.byteswap()的作用。

你需要改变字节顺序的常见情况是:

  1. 您的数据和dtype字节序不匹配,并且您要更改dtype以使其与数据匹配。
  2. 您的数据和dtype字节序不匹配,并且您要交换数据,以便它们匹配dtype
  3. 你的数据和dtype endianess匹配,但你想要的数据交换和dtype反映这一点

Data and dtype endianness don’t match, change dtype to match data

我们做一些他们不匹配的东西:

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

这种情况的明显修复是更改dtype,以便它给出正确的字节序:

>>> fixed_end_dtype_arr = wrong_end_dtype_arr.newbyteorder()
>>> fixed_end_dtype_arr[0]
1

注意数组在内存中没有更改:

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

Data and type endianness don’t match, change data to match dtype

你可能想这样做,如果你需要内存中的数据是一定的顺序。例如,你可能正在将内存写入需要某个字节顺序的文件。

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

现在数组在内存中已更改

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

Data and dtype endianness match, swap data and dtype

你可能有一个正确指定的数组dtype,但你需要数组在内存中有相反的字节顺序,你想让dtype匹配,所以数组值是有意义的。在这种情况下,你只需要做前面的两个操作:

>>> swapped_end_arr = big_end_arr.byteswap().newbyteorder()
>>> swapped_end_arr[0]
1
>>> swapped_end_arr.tobytes() == big_end_str
False

使用ndarray astype方法可以实现将数据转换为特定类型和字节顺序的更简单方法:

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