struct – バイナリデータを扱う

目的:文字列とバイナリデータを変換する
利用できるバージョン:1.4 以上

struct モジュールはバイト文字列と、数値や文字列といったネイティブの Python データ型を変換する関数を提供します。

関数 対 Struct クラス

構造化された値を扱うモジュールレベルの関数セットに加えて Struct クラス(Python 2.5 で追加)もあります。フォーマット指定子は、正規表現のように文字列フォーマットからコンパイルされた表現に変換されます。その変換処理はリソースを消費します。そのため Struct インスタンスを作成して、モジュールレベルの関数を使用せずにインスタンスメソッドを呼び出す方が普通は効率が良いです。この後の全サンプルは Struct クラスを使用します。

パックとアンパック

Struct は文字列へデータを パック します。そして、そのデータ型の特性により表現されるフォーマット指定子を用いて、文字列からデータを アンパック します。また、オプションでサイズやエンディアンも指定できます。詳細は 標準ライブラリドキュメント を参照してください。

このサンプルでは、フォーマット指定子は整数(int か long)、2文字の文字列、浮動小数で呼び出されます。フォーマット指定子の間にスペースが含まれていますが、そのフォーマットがコンパイルされるときに無視されます。

import struct
import binascii

values = (1, 'ab', 2.7)
s = struct.Struct('I 2s f')
packed_data = s.pack(*values)

print 'Original values:', values
print 'Format string  :', s.format
print 'Uses           :', s.size, 'bytes'
print 'Packed Value   :', binascii.hexlify(packed_data)

このサンプルは、パックされた値を binascii.hexlify() で表示するために16進数のバイトシーケンスに変換します。文字によってはヌル(null)になります。

$ python struct_pack.py
Original values: (1, 'ab', 2.7)
Format string  : I 2s f
Uses           : 12 bytes
Packed Value   : 0100000061620000cdcc2c40

unpack() にパックされた値を渡すと、基本的には同じ値が返されます(浮動小数が不一致なのに注意)。

import struct
import binascii

packed_data = binascii.unhexlify('0100000061620000cdcc2c40')

s = struct.Struct('I 2s f')
unpacked_data = s.unpack(packed_data)
print 'Unpacked Values:', unpacked_data
$ python struct_unpack.py
Unpacked Values: (1, 'ab', 2.700000047683716)

エンディアン

デフォルトでは、値はネイティブの C 言語ライブラリの “エンディアン” 表記でエンコードされます。それはフォーマット文字列にエンディアンディレクティブを明示的に指定することで簡単にオーバーライドできます。

import struct
import binascii

values = (1, 'ab', 2.7)
print 'Original values:', 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 'Format string  :', s.format, 'for', name
    print 'Uses           :', s.size, 'bytes'
    print 'Packed Value   :', binascii.hexlify(packed_data)
    print 'Unpacked Value :', s.unpack(packed_data)
$ python struct_endianness.py
Original values: (1, 'ab', 2.7)

Format string  : @ I 2s f for native, native
Uses           : 12 bytes
Packed Value   : 0100000061620000cdcc2c40
Unpacked Value : (1, 'ab', 2.700000047683716)

Format string  : = I 2s f for native, standard
Uses           : 10 bytes
Packed Value   : 010000006162cdcc2c40
Unpacked Value : (1, 'ab', 2.700000047683716)

Format string  : < I 2s f for little-endian
Uses           : 10 bytes
Packed Value   : 010000006162cdcc2c40
Unpacked Value : (1, 'ab', 2.700000047683716)

Format string  : > I 2s f for big-endian
Uses           : 10 bytes
Packed Value   : 000000016162402ccccd
Unpacked Value : (1, 'ab', 2.700000047683716)

Format string  : ! I 2s f for network
Uses           : 10 bytes
Packed Value   : 000000016162402ccccd
Unpacked Value : (1, 'ab', 2.700000047683716)

バッファ

パックされたバイナリデータを扱うのは、普通は高いパフォーマンスを要求されるか、拡張モジュールの外部でデータを受け渡すときです。そういった状況では、それぞれのパックされた構造でバッファを割り当てないことでオーバーヘッドを減らして最適化できます。 pack_into()unpack_from() メソッドは、あらかじめ割り当てられたバッファに直接書き込めます。

import struct
import binascii

s = struct.Struct('I 2s f')
values = (1, 'ab', 2.7)
print 'Original:', values

print
print 'ctypes string buffer'

import ctypes
b = ctypes.create_string_buffer(s.size)
print 'Before  :', binascii.hexlify(b.raw)
s.pack_into(b, 0, *values)
print 'After   :', binascii.hexlify(b.raw)
print 'Unpacked:', s.unpack_from(b, 0)

print
print 'array'

import array
a = array.array('c', '\0' * s.size)
print 'Before  :', binascii.hexlify(a)
s.pack_into(a, 0, *values)
print 'After   :', binascii.hexlify(a)
print 'Unpacked:', s.unpack_from(a, 0)

Structsize 属性は必要なバッファがどのぐらいかを伝えます。

$ python struct_buffers.py
Original: (1, 'ab', 2.7)

ctypes string buffer
Before  : 000000000000000000000000
After   : 0100000061620000cdcc2c40
Unpacked: (1, 'ab', 2.700000047683716)

array
Before  : 000000000000000000000000
After   : 0100000061620000cdcc2c40
Unpacked: (1, 'ab', 2.700000047683716)

See also

struct
本モジュールの標準ライブラリドキュメント
array
型を固定した値のシーケンスを扱う array モジュール
binascii
バイナリデータの ASCII 表現を生成する binascii モジュール
WikiPedia: Endianness
エンコーディングのエンディアンとバイトオーダの説明
インメモリデータ構造
データ構造と連携すつその他のツール
Bookmark and Share