位元組交換#

位元組順序與 ndarray 簡介#

ndarray 是一個物件,提供記憶體中資料的 python 陣列介面。

經常發生您想要用陣列檢視的記憶體,其位元組順序與您執行 Python 的電腦不同。

例如,我可能正在使用具有小端 CPU 的電腦(例如 Intel Pentium),但我從大端電腦寫入的檔案載入了一些資料。假設我從 Sun(大端)電腦寫入的檔案中載入了 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_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() 的作用。

您需要變更位元組順序的常見情況是

  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]
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

將資料轉換為特定 dtype 和位元組順序的更簡單方法是使用 ndarray astype 方法

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