16進数のプロトコルを扱うコードを書くとき、Python では struct.pack を必ずと言っていいほど使う。書き方はいろいろあるのに、全然覚えられない。たとえば
Python
struct.pack('>H', checksum)
data = struct.pack('>I', id)
1. バイトオーダーとアライメント(先頭文字)
フォーマット文字列の最初の文字は、通常バイトオーダーとアライメントを表す:
| 文字 | 説明 | 例 |
|---|---|---|
@ | デフォルト(ネイティブバイトオーダー、アライメントなし) | @I |
= | ネイティブバイトオーダー、標準サイズ | =H |
< | リトルエンディアン(Little-Endian) | <i |
> | ビッグエンディアン(Big-Endian) | >f |
! | ネットワークバイトオーダー(ビッグエンディアンと同じ) | !d |
2. データ型とサイズ
| 文字 | C 型 | Python 型 | サイズ(バイト) | 例 |
|---|---|---|---|---|
x | パディングバイト | なし | 1 | x |
c | char | 長さ1のバイト列 | 1 | c |
b | signed char | 整数 | 1 | b |
B | unsigned char | 整数 | 1 | B |
? | _Bool | 真偽値 | 1 | ? |
h | short | 整数 | 2 | h |
H | unsigned short | 整数 | 2 | H |
i | int | 整数 | 4 | i |
I | unsigned int | 整数 | 4 | I |
l | long | 整数 | 4 | l (32ビットシステム) |
L | unsigned long | 整数 | 4 | L (32ビットシステム) |
q | long long | 整数 | 8 | q |
Q | unsigned long long | 整数 | 8 | Q |
f | float | 浮動小数点数 | 4 | f |
d | double | 浮動小数点数 | 8 | d |
s | char[] | バイト列 | 数値プレフィックスで指定する長さ | 10s |
p | Pascal文字列 | バイト列 | 長さ+1バイト(最大255) | p |
P | void* | 整数 | プラットフォーム依存 | P |
3. 特殊記号
| 文字 | 説明 | 例 |
|---|---|---|
0 | パディングバイト(x と同じ) | 0x |
num | 数値プレフィックス。繰り返し回数または長さを表す | 3I は符号なし整数を3つパックする |
_ | プラットフォームのネイティブサイズとアライメント(Python 3.3+ が必要) | _d |
4. よく使う組み合わせの例
ビッグエンディアンの4バイト符号なし整数 + 倍精度浮動小数点数をパックする:
Pythondata = struct.pack('>Id', 0x1234, 3.14)パディング付きのリトルエンディアン構造体をパックする(例:
int + char、4バイトアライメント):Pythondata = struct.pack('<ic', 42, b'A') # 自动填充 1 字节固定長の文字列をパックする:
Pythondata = struct.pack('10s', b'hello') # 补零到 10 字节ブール値 + 符号なし短整数をパックする(ネットワークバイトオーダー):
Pythondata = struct.pack('!?H', True, 0xABCD)
5. 注意点
数値範囲の検証:
- たとえば
B(0〜255)やI(0〜0xFFFFFFFF)は、範囲を超えるとエラーになる。 パックする前に数値の範囲をチェックすることをおすすめします:
Pythonif not (0 <= value <= 0xFFFF): raise ValueError("数值超出范围")
- たとえば
プラットフォームによる違い:
lとLは32ビットシステムでは4バイト、64ビットシステムでは8バイトになることがある。- 確実に4バイトにしたい場合は
iまたはIを使う。
文字列の扱い:
sフォーマットでは長さを明示する必要がある(例:10s)。長さが動的に変わる文字列が必要な場合は、
len()と組み合わせて使う:Pythons = b'hello' data = struct.pack(f'I{len(s)}s', len(s), s)
struct.pack の fmt ドキュメント