程式扎記: [Python Std Library] String services : struct — Interpret strings as packed binary data

標籤

2012年3月26日 星期一

[Python Std Library] String services : struct — Interpret strings as packed binary data


轉載自 這裡
Preface :
在某些時候你可能會需要對 Binary 的資料或是來自網路的 Traffic 進行 Parsing. 在有固定 Format 的情況下, 你可以利用此模組自動幫你進行切割與分析. 底下是原文說明 :
This module performs conversions between Python values and C structs represented as Python strings. This can be used in handling binary data stored in files or from network connections, among other sources. It uses Format Strings as compact descriptions of the layout of the C structs and the intended conversion to/from Python values.

Functions and Exceptions :
在 struct 模組定義以下的例外與函數 :
- exception struct.error
Exception raised on various occasions; argument is a string describing what is wrong.

struct.pack(fmtv1v2, ...)
根據 fmt 指定將後面的 v1v2... 組合起來並以 binary 返回. 如果 fmt 與 v1v2... 等長度或是數目不符, 會拋出 struct.error.
>>> struct.pack('fh', 1.0, 12)
b'\x00\x00\x80?\x0c\x00'

struct.pack_into(fmtbufferoffsetv1v2...)
根據 fmt 將 v1v2... 以 offset 指定的開始位置 pack 到 buffer 中. (New in version 2.5.)
>>> from ctypes import create_string_buffer
>>> b = create_string_buffer(10)
>>> b.raw
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>> import struct
>>> struct.pack_into("hhh", b, 0, 1, 2, -1) # h->short(2)
>>> b.raw
b'\x01\x00\x02\x00\xff\xff\x00\x00\x00\x00'

struct.unpack(fmtstring)
將 string 內容以 fmt 規定進行分析並將每個分割的內容以 tuple 回傳. 要注意的是 string 的長度 (in bytes) 必須與 fmt 分析的長度一致 ; 可以使用 len(string) = calcsize(fmt) 檢驗.
>>> bs = struct.pack('hh', 1, 2)
>>> struct.unpack('hh', bs)
(1, 2)
>>> len(bs)
4
>>> struct.calcsize('hh')
4

struct.unpack_from(fmt, buffer[, offset=0])
New in version 2.5.
根據 fmt 對位置在 offset 指定 buffer 開始位置進行 unpack. (len(buffer[offset:]) must be at least calcsize(fmt))
>>> b = create_string_buffer(10)
>>> b.raw
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>> struct.pack_into("hhh", b, 2, 1, 2, -1)
>>> b.raw
b'\x00\x00\x01\x00\x02\x00\xff\xff\x00\x00'
>>> struct.unpack_from("hhh", b, 2)
(1, 2, -1)

struct.calcsize(fmt)
分析 fmt 預期要分析的資料長度 (in byte).
>>> struct.calcsize('ihL') # i->int(4) ; h->short(2) ; L->unsigned long(4). 4+2+4=12
12

Format Strings :
struct 模組利用定義好的 Format Characters 對資料進行分析與切割, 除此之外有所謂的 Big-endian, Little-endian 也可以參考 Byte Order, Size, and Alignment 進行設定.

- Byte Order, Size, and Alignment
在使用 Format Characters 時, 第一個字元可以用來指定 Byte Order. 而支援的類型可以參考下表 :

(如果第一個字元沒有指定成上述類型, 預設是使用 '@')

Native byte order 是平台相依的, 不同的平台可能是 Big-endian 或是 Little-endia :
Native byte order is big-endian or little-endian, depending on the host system. For example, Intel x86 and AMD64 (x86-64) are little-endian; Motorola 68000 and PowerPC G5 are big-endian; ARM and Intel Itanium feature switchable endianness (bi-endian). Use sys.byteorder to check the endianness of your system.
>>> import sys
>>> sys.byteorder
'little'

- Format Characters
底下是支援資料格式的列表, 你可以組合來定義如何解析資料 :


底下是使用注意事項 :
1. The '?' conversion code corresponds to the _Bool type defined by C99. If this type is not available, it is simulated using a char. In standard mode, it is always represented by one byte. (New in version 2.6.)
2. The 'q' and 'Q' conversion codes are available in native mode only if the platform C compiler supports C long long, or, on Windows, __int64. They are always available in standard modes. (New in version 2.2.)
3. When attempting to pack a non-integer using any of the integer conversion codes, if the non-integer has a __index__() method then that method is called to convert the argument to an integer before packing. If no __index__() method exists, or the call to __index__() raises TypeError, then the __int__() method is tried. However, the use of __int__() is deprecated, and will raise DeprecationWarning.
4. For the 'f' and 'd' conversion codes, the packed representation uses the IEEE 754 binary32 (for 'f') or binary64 (for 'd') format, regardless of the floating-point format used by the platform.
5. The 'P' format character is only available for the native byte ordering (selected as the default or with the '@' byte order character). The byte order character '=' chooses to use little- or big-endian ordering based on the host system. The struct module does not interpret this as native ordering, so the 'P' format is not available.

在使用這些格式字元, 你可以在前面加上數字說明 repeat 的次數. 如 '4h' = 'hhhh' ; 另外如果你在格式字元間夾帶 White space 將會被忽略.

- Examples
底下的範例假設使用 native byte order, 並且 alignment 是 big-endian 的 machine. 先來看看簡單 unpack/pack 三個整數的範例 :
>>> from struct import *
>>> pack('hhl', 1, 2, 3)
b'\x01\x00\x02\x00\x03\x00\x00\x00'
>>> unpack('hhl', b'\x01\x00\x02\x00\x03\x00\x00\x00')
(1, 2, 3)
>>> calcsize('hhl')
8

更方便的是你可以透過 unpack 回來的 tuple, 用 Python 的語法一一分配給對應變數, 或是使用 namedtuple :


接著我們來看看 little-endian 與 big-endian 的差別 :
>>> import sys
>>> from struct import *
>>> sys.byteorder # 我的 OS 使用 little-endian
'little'
>>> pack('llh', 1, 2, 3)
b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00'
>>> pack('>llh', 1, 2, 3) # 使用 big-endian 包裝
b'\x00\x00\x00\x01\x00\x00\x00\x02\x00\x03'


2 則留言:

  1. 找struct的使用範例 竟然Google到奎哥的文章 XD

    回覆刪除
    回覆
    1. 哈~ 牛哥英文這麼好, 直接看 Python 官方網站上面的說明會更清楚喔 ^^

      刪除

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!