Python標準庫筆記(6) — struct模組
目錄[-]
該模組作用是完成Python數值和C語言結構體的Python字串形式間的轉換。這可以用於處理儲存在檔案中或從網路連線中儲存的二進位制資料,以及其他資料來源。
用途: 在Python基本資料型別和二進位制資料之間進行轉換
struct
模組提供了用於在位元組字串和Python原生資料型別之間轉換函式,比如數字和字串。
模組函式和Struct類
它除了提供一個Struct
類之外,還有許多模組級的函式用於處理結構化的值。這裡有個格式符(Format specifiers)的概念,是指從字串格式轉換為已編譯的表示形式,類似於正則表示式的處理方式。通常例項化Struct
類,呼叫類方法來完成轉換,比直接呼叫模組函式有效的多。下面的例子都是使用Struct
Packing(打包)和Unpacking(解包)
Struct
支援將資料packing(打包)成字串,並能從字串中逆向unpacking(解壓)出資料。
在本例中,格式指定器(specifier)需要一個整型或長整型,一個兩個位元組的string,和一個浮點數。格式符中的空格用於分隔各個指示器(indicators),在編譯格式時會被忽略。
import struct import binascii values = (1, 'ab'.encode('utf-8'), 2.7) s = struct.Struct('I 2s f') packed_data = s.pack(*values) print('原始值:', values) print('格式符:', s.format) print('佔用位元組:', s.size) print('打包結果:', binascii.hexlify(packed_data))
# output
原始值: (1, b'ab', 2.7)
格式符: b'I 2s f'
佔用位元組: 12
打包結果: b'0100000061620000cdcc2c40'
這個示例將打包的值轉換為十六進位制位元組序列,用binascii.hexlify()
方法打印出來。
使用unpack()
方法解包。
import struct import binascii packed_data = binascii.unhexlify(b'0100000061620000cdcc2c40') s = struct.Struct('I 2s f') unpacked_data = s.unpack(packed_data) print('解包結果:', unpacked_data)
# output
解包結果: (1, b'ab', 2.700000047683716)
將打包的值傳給unpack()
,基本上返回相同的值(浮點數會有差異)。
位元組順序/大小/對齊
預設情況下,pack是使用本地C庫的位元組順序來編碼的。格式化字串的第一個字元可以用來表示填充資料的位元組順序、大小和對齊方式,如下表所描述的:
Character |
Byte order |
Size |
Alignment |
---|---|---|---|
@ |
本地 |
本地 |
本地 |
= |
本地 |
standard |
none |
< |
little-endian(小位元組序) |
standard |
none |
> |
big-endian(大位元組序) |
standard |
none |
! |
network (= big-endian) |
standard |
none |
如果格式符中沒有設定這些,那麼預設將使用 @
。
本地位元組順序是指位元組順序是由當前主機系統決定。比如:Intel x86和AMD64(x86-64)使用小位元組序; Motorola 68000和 PowerPC G5使用大位元組序。ARM和Intel安騰支援切換位元組序。可以使用sys.byteorder
檢視當前系統的位元組順序。
本地大小(Size)和對齊(Alignment)是由c編譯器的sizeof
表示式確定的。它與本地位元組順序對應。
標準大小由格式符確定,下面會講各個格式的標準大小。
示例:
import struct
import binascii
values = (1, 'ab'.encode('utf-8'), 2.7)
print('原始值 : ', values)
endianness = [
('@', 'native, native'),
('=', 'native, standard'),
('<', 'little-endian'),
('>', 'big-endian'),
('!', 'network'),
]
for code, name in endianness:
s = struct.Struct(code + ' I 2s f')
packed_data = s.pack(*values)
print()
print('格式符 : ', s.format, 'for', name)
print('佔用位元組: ', s.size)
print('打包結果: ', binascii.hexlify(packed_data))
print('解包結果: ', s.unpack(packed_data))
# output
原始值 : (1, b'ab', 2.7)
格式符 : b'@ I 2s f' for native, native
佔用位元組: 12
打包結果: b'0100000061620000cdcc2c40'
解包結果: (1, b'ab', 2.700000047683716)
格式符 : b'= I 2s f' for native, standard
佔用位元組: 10
打包結果: b'010000006162cdcc2c40'
解包結果: (1, b'ab', 2.700000047683716)
格式符 : b'< I 2s f' for little-endian
佔用位元組: 10
打包結果: b'010000006162cdcc2c40'
解包結果: (1, b'ab', 2.700000047683716)
格式符 : b'> I 2s f' for big-endian
佔用位元組: 10
打包結果: b'000000016162402ccccd'
解包結果: (1, b'ab', 2.700000047683716)
格式符 : b'! I 2s f' for network
佔用位元組: 10
打包結果: b'000000016162402ccccd'
解包結果: (1, b'ab', 2.700000047683716)
格式符
格式符對照表如下:
Format |
C Type |
Python type |
Standard size |
Notes |
---|---|---|---|---|
x |
pad byte |
no value |
||
c |
char |
bytes of length 1 |
1 |
|
b |
signed char |
integer |
1 |
(1),(3) |
B |
unsigned char |
integer |
1 |
(3) |
? |
_Bool |
bool |
1 |
(1) |
h |
short |
integer |
2 |
(3) |
H |
unsigned short |
integer |
2 |
(3) |
i |
int |
integer |
4 |
(3) |
I |
unsigned int |
integer |
4 |
(3) |
l |
long |
integer |
4 |
(3) |
L |
unsigned long |
integer |
4 |
(3) |
q |
long long |
integer |
8 |
(2), (3) |
Q |
unsigned long long |
integer |
8 |
(2), (3) |
n |
ssize_t |
integer |
(4) |
|
N |
size_t |
integer |
(4) |
|
f |
float |
float |
4 |
(5) |
d |
double |
float |
8 |
(5) |
s |
char[] |
bytes |
||
p |
char[] |
bytes |
||
P |
void * |
integer |
(6) |
緩衝區
將資料打包成二進位制通常是用在對效能要求很高的場景。 在這類場景中可以通過避免為每個打包結構分配新緩衝區的開銷來優化。 pack_into()
和unpack_from()
方法支援直接寫入預先分配的緩衝區。
import array
import binascii
import ctypes
import struct
s = struct.Struct('I 2s f')
values = (1, 'ab'.encode('utf-8'), 2.7)
print('原始值:', values)
print()
print('使用ctypes模組string buffer')
b = ctypes.create_string_buffer(s.size)
print('原始buffer :', binascii.hexlify(b.raw))
s.pack_into(b, 0, *values)
print('打包結果寫入 :', binascii.hexlify(b.raw))
print('解包 :', s.unpack_from(b, 0))
print()
print('使用array模組')
a = array.array('b', b'