Python標準庫筆記(6) — struct模塊

該模塊做用是完成Python數值和C語言結構體的Python字符串形式間的轉換。這能夠用於處理存儲在文件中或從網絡鏈接中存儲的二進制數據,以及其餘數據源。python

用途: 在Python基本數據類型和二進制數據之間進行轉換正則表達式

struct模塊提供了用於在字節字符串和Python原生數據類型之間轉換函數,好比數字和字符串。bash

模塊函數和Struct類

它除了提供一個Struct類以外,還有許多模塊級的函數用於處理結構化的值。這裏有個格式符(Format specifiers)的概念,是指從字符串格式轉換爲已編譯的表示形式,相似於正則表達式的處理方式。一般實例化Struct類,調用類方法來完成轉換,比直接調用模塊函數有效的多。下面的例子都是使用Struct類。網絡

Packing(打包)和Unpacking(解包)

Struct支持將數據packing(打包)成字符串,並能從字符串中逆向unpacking(解壓)出數據。ide

在本例中,格式指定器(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庫的字節順序來編碼的。格式化字符串的第一個字符能夠用來表示填充數據的字節順序、大小和對齊方式,以下表所描述的:code

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'\0' * s.size)
print('原始值   :', binascii.hexlify(a))
s.pack_into(a, 0, *values)
print('打包寫入 :', binascii.hexlify(a))
print('解包     :', s.unpack_from(a, 0))
# output
原始值: (1, b'ab', 2.7)

使用ctypes模塊string buffer
原始buffer  : b'000000000000000000000000'
打包結果寫入 : b'0100000061620000cdcc2c40'
解包        : (1, b'ab', 2.700000047683716)

使用array模塊
原始值   : b'000000000000000000000000'
打包寫入 : b'0100000061620000cdcc2c40'
解包     : (1, b'ab', 2.700000047683716)

博客原文: http://www.spiderpy.cn/blog/detail/37

相關文章
相關標籤/搜索