10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

structとは

structモジュールは、Pythonオブジェクトとバイナリデータ(bytes)を相互変換する。

ファイルフォーマットの解析やネットワークプロトコルの処理で使うことが多い。

import struct

基本的な使い方

パック(Python → bytes)

packed = struct.pack('i', 42)  # int
print(packed)  # b'*\x00\x00\x00'

アンパック(bytes → Python)

unpacked = struct.unpack('i', packed)
print(unpacked)  # (42,)  ← タプルで返る

複数の値

# パック
packed = struct.pack('iif', 10, 20, 3.14)

# アンパック
a, b, c = struct.unpack('iif', packed)
print(a, b, c)  # 10 20 3.14...

フォーマット文字

フォーマット C型 サイズ
b signed char 1
B unsigned char 1
h short 2
H unsigned short 2
i int 4
I unsigned int 4
q long long 8
Q unsigned long long 8
f float 4
d double 8
s char[] 指定サイズ
x パディング 1

バイトオーダー

value = 0x12345678

# リトルエンディアン(Intel等)
struct.pack('<I', value)  # b'xV4\x12'

# ビッグエンディアン
struct.pack('>I', value)  # b'\x124Vx'

# ネットワーク(= ビッグエンディアン)
struct.pack('!I', value)  # b'\x124Vx'
プレフィックス バイトオーダー
< リトルエンディアン
> ビッグエンディアン
! ネットワーク
= ネイティブ
@ ネイティブ(アラインメント付き)

文字列

# 固定長文字列
packed = struct.pack('5s', b'Hello')

# パディング付き
packed = struct.pack('10s', b'Hi')  # 残りは\x00

# アンパック
name, = struct.unpack('10s', packed)
name = name.rstrip(b'\x00').decode()

構造体の表現

C言語の構造体:

struct Point {
    int x;
    int y;
};

Pythonで表現:

point = struct.pack('ii', 100, 200)

x, y = struct.unpack('ii', point)

複雑な構造体

struct Record {
    char name[10];
    int age;
    float score;
};
record = struct.pack('10sif', b'Alice', 25, 95.5)

name, age, score = struct.unpack('10sif', record)
name = name.rstrip(b'\x00').decode()

Structクラス

繰り返し処理には事前コンパイルが効率的:

# フォーマットをコンパイル
point_struct = struct.Struct('ii')

# 複数のポイントを処理
points = [(10, 20), (30, 40), (50, 60)]
packed = b''.join(point_struct.pack(x, y) for x, y in points)

# アンパック
for data in struct.iter_unpack('ii', packed):
    print(data)

実践例

BMPファイルヘッダ

BMP_HEADER = struct.Struct('<2sIHHI')

# ヘッダを解析
with open('image.bmp', 'rb') as f:
    header = f.read(BMP_HEADER.size)
    magic, size, _, _, offset = BMP_HEADER.unpack(header)
    print(f"マジック: {magic}, サイズ: {size}")

ネットワークパケット

def create_packet(msg_type: int, data: bytes) -> bytes:
    # | Type (1 byte) | Length (2 bytes) | Data |
    header = struct.pack('!BH', msg_type, len(data))
    return header + data

def parse_packet(packet: bytes) -> tuple:
    msg_type, length = struct.unpack('!BH', packet[:3])
    data = packet[3:3 + length]
    return msg_type, data

# 使用
packet = create_packet(1, b"Hello!")
msg_type, data = parse_packet(packet)

PNGシグネチャ

PNG_SIGNATURE = b'\x89PNG\r\n\x1a\n'

with open('image.png', 'rb') as f:
    signature = f.read(8)
    if signature == PNG_SIGNATURE:
        print("Valid PNG file")

calcsize

フォーマットのサイズを取得:

struct.calcsize('i')    # 4
struct.calcsize('iif')  # 12
struct.calcsize('10s')  # 10
struct.calcsize('!IH')  # 6

注意点

1. アラインメント

# ネイティブはパディングが入る
struct.calcsize('@biq')  # 16(パディング含む)

# リトルエンディアンはパディングなし
struct.calcsize('<biq')  # 13

2. 文字列はバイト列

# ❌
struct.pack('5s', "Hello")  # TypeError

# ✓
struct.pack('5s', b"Hello")

3. 戻り値はタプル

# 単一の値でもタプル
result = struct.unpack('i', data)  # (42,)
value = result[0]  # または
value, = struct.unpack('i', data)

まとめ

用途 関数
パック struct.pack(fmt, ...)
アンパック struct.unpack(fmt, bytes)
サイズ取得 struct.calcsize(fmt)
繰り返しアンパック struct.iter_unpack(fmt, bytes)
事前コンパイル struct.Struct(fmt)

バイナリデータを扱うときはstructモジュールを使いましょう!

10
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?