struct --- バイト列をパックされたバイナリデータとして解釈する

ソースコード: Lib/struct.py


このモジュールは、Python の値と、Python の bytes オブジェクトで表した C の構造体との間の変換を行います。 簡潔な 書式文字列 で、Python の値との変換方法を記述します。 このモジュールの関数とオブジェクトには、大きく分けて 2 つの用途があります。 外部のデータ源 (ファイルやネットワーク接続) とのデータ交換と、Python アプリケーションと C の層との間のデータ転送です。

注釈

接頭文字を指定しなければ、デフォルトはネイティブモードです。 Python インタープリタのビルドに使われたプラットフォームとコンパイラに基づいてデータをパックまたはアンパックします。 C の構造体をパックした結果には、各 C 型の適切なアラインメントを維持するパディングバイトが含まれます。 アンパック時も同様にアラインメントを考慮します。 一方、外部のデータ源とのデータ通信では、バイトオーダと要素間のパディングをプログラマーが定義する必要があります。 詳しくは バイトオーダ、サイズ、アラインメント を参照してください。

struct のいくつかの関数 (と Struct のメソッド) は buffer 引数を受け取ります。 この引数には バッファプロトコル (buffer Protocol) を実装し、読み出し可能または読み書き可能なバッファを提供するオブジェクトを指定します。 よく使われるのは bytes と bytearray ですが、バイトの配列として扱える多くの型もバッファプロトコルを実装しています。 そのため、bytes オブジェクトからの余分なコピーなしに読み書きできます。

関数と例外

このモジュールは以下の例外と関数を定義しています:

exception struct.error

様々な状況で送出される例外です。引数は何が問題なのかを記述する文字列です。

struct.pack(format, v1, v2, ...)

書式文字列 format に従って値 v1、v2、... をパックしたバイト列オブジェクトを返します。 引数は、書式が要求する値に正確に対応していなければなりません。

struct.pack_into(format, buffer, offset, v1, v2, ...)

書式文字列 format に従って値 v1、v2、... をパックし、書き込み可能なバッファ buffer の位置 offset から書き込みます。 offset は必須の引数です。 負の offset は buffer の末尾から数えます。

struct.unpack(format, buffer)

(pack(format, ...) でパックされたであろう) バッファ buffer を、書式文字列 format に従ってアンパックします。 値が一つしかない場合を含め、結果はタプルで返されます。 バッファのバイトサイズは、 calcsize() の返り値である書式文字列が要求するサイズと一致しなければなりません。

struct.unpack_from(format, /, buffer, offset=0)

書式文字列 format に従い、buffer の位置 offset からアンパックします。 結果は、要素が 1 つだけでもタプルになります。 位置 offset から先のバッファのバイト数は、書式が必要とするサイズ (calcsize() の戻り値) 以上でなければなりません。 負の offset は buffer の末尾から数えます。

struct.iter_unpack(format, buffer)

バッファ buffer を、書式文字列 format に従ってイテレータ形式でアンパックします。 この関数が返すイテレータは、すべての内容を読み終わるまでバッファから一定の大きさのチャンクを読み取ります。 バッファのバイトサイズは、 calcsize() の返り値である書式文字列が要求するサイズの倍数でなければなりません。

イテレーション毎に書式文字列で指定されたタプルを yield します。

Added in version 3.4.

struct.calcsize(format)

書式文字列 format に従って、構造体 (それと pack(format, ...) によって作成されるバイト列オブジェクト) のサイズを返します。

書式文字列

書式文字列は、パックとアンパックの際のデータの配置を記述します。 パックまたはアンパックするデータの型を指定する 書式指定文字 を組み合わせて作ります。 さらに、特殊な文字で バイトオーダ、サイズ、アラインメント を制御します。 各書式文字列は、データ全体の性質を記述する省略可能な接頭文字と、実際の値やパディングを記述する 1 つ以上の書式指定文字からなります。

バイトオーダ、サイズ、アラインメント

デフォルトでは、C の型をマシンのネイティブな形式とバイトオーダで表し、必要に応じてパディングバイトを挟んで適切に配置します (C コンパイラの規則に従います)。 この動作によって、パックされた構造体のバイト列は、対応する C の構造体のメモリ配置と正確に一致します。 ネイティブのバイトオーダとパディングを使うか、標準の形式を使うかは用途によります。

これに代わって、フォーマット文字列の最初の文字を使って、バイトオーダやサイズ、アラインメントを指定することができます。指定できる文字を以下のテーブルに示します:

文字

バイトオーダ

サイズ

アラインメント

@

native

native

native

=

native

standard

none

<

リトルエンディアン

standard

none

>

ビッグエンディアン

standard

none

!

ネットワーク (= ビッグエンディアン)

standard

none

フォーマット文字列の最初の文字が上のいずれかでない場合、'@' であるとみなされます。

注釈

数値 1023 (16 進数では 0x3ff) は、次のバイト列で表されます:

  • ビッグエンディアン (>) では 03 ff

  • リトルエンディアン (<) では ff 03

Python の例:

>>> import struct
>>> struct.pack('>h', 1023)
b'\x03\xff'
>>> struct.pack('<h', 1023)
b'\xff\x03'

ネイティブのバイトオーダはビッグエンディアンかリトルエンディアンで、ホストシステムに依存します。例えば、Intel x86、AMD64 (x86-64) および Apple M1 はリトルエンディアンです。IBM z および多くの古いアーキテクチャはビッグエンディアンです。使っているシステムでのエンディアンは sys.byteorder を使って調べて下さい。

ネイティブのサイズおよびアラインメントは C コンパイラの sizeof 式で決定されます。ネイティブのサイズおよびアラインメントはネイティブのバイトオーダと同時に使われます。

標準のサイズはフォーマット文字だけで決まります。 書式指定文字 の表を参照して下さい。

'@' と '=' の違いに注意してください: 両方ともネイティブのバイトオーダですが、後者のバイトサイズとアラインメントは標準のものに合わせてあります。

'!' はネットワークバイトオーダを表します。 IETF RFC 1700 で定義されているとおり、常にビッグエンディアンです。

バイトオーダに関して、「(強制的にバイトスワップを行う)ネイティブの逆」を指定する方法はありません。'<' または '>' のうちふさわしい方を選んでください。

注釈:

  1. パディングは構造体のメンバの並びの中にだけ自動で追加されます。最初や最後にパディングが追加されることはありません。

  2. ネイティブでないサイズおよびアラインメントが使われる場合にはパディングは行われません (たとえば '<', '>', '=', '!' を使った場合です)。

  3. 特定の型によるアラインメント要求に従うように構造体の末端をそろえるには、繰り返し回数をゼロにした特定の型でフォーマットを終端します。 使用例 を参照して下さい。

書式指定文字

書式指定文字には以下の意味があります。 C と Python の値の変換関係は、表に示すそれぞれの型から分かります。 「標準のサイズ」列は standard サイズ使用時にパックされた値が何バイトかを示します。 つまり、フォーマット文字列が '<', '>', '!', '=' のいずれかで始まっている場合のものです。 native サイズ使用時にはパックされた値の大きさはプラットフォーム依存です。

フォーマット

C の型

Python の型

標準のサイズ

注釈

x

パディングバイト

値なし

(7)

c

char

長さ 1 のバイト列

1

b

signed char

整数

1

(2)

B

unsigned char

整数

1

(2)

?

_Bool

真偽値型(bool)

1

(1)

h

short

整数

2

(2)

H

unsigned short

整数

2

(2)

i

int

整数

4

(2)

I

unsigned int

整数

4

(2)

l

long

整数

4

(2)

L

unsigned long

整数

4

(2)

q

long long

整数

8

(2)

Q

unsigned long long

整数

8

(2)

n

ssize_t

整数

(2), (3)

N

size_t

整数

(2), (3)

e

_Float16

浮動小数点数

2

(4), (6)

f

float

浮動小数点数

4

(4)

d

double

浮動小数点数

8

(4)

Zf

float complex

複素数

8

(10)

Zd

double complex

複素数

16

(10)

s

char[]

bytes

(9)

p

char[]

bytes

(8)

P

void*

整数

(2), (5)

バージョン 3.3 で変更: 'n' および 'N' フォーマットのサポートが追加されました。

バージョン 3.6 で変更: 'e' フォーマットのサポートが追加されました。

バージョン 3.14 で変更: 'F' と 'D' の書式がサポートされました。

バージョン 3.15 で変更: 'Zf' と 'Zd' の書式がサポートされました。 'F' と 'D' の書式は 緩やかに非推奨 になりました。

参考

array モジュール、ctypes モジュール、numpy などのサードパーティーモジュールでも、似ていますが少し異なる型コードを使います。

注釈:

  1. '?' 変換コードは、C99 以降の C 規格で定義されている _Bool 型に対応します。 標準モードでは 1 バイトで表します。

  2. 整数の変換コードで整数以外の値をパックしようとした場合、その値に __index__() メソッドがあれば、パックする前にそのメソッドを呼び出して引数を整数に変換します。

    バージョン 3.2 で変更: 整数以外の値に対して __index__() メソッドを使うようになりました。

  3. 'n' および 'N' 変換コードは (デフォルトもしくはバイトオーダ文字 '@' 付きで選択される) native サイズ使用時のみ利用できます。standard サイズ使用時には、自身のアプリケーションに適する他の整数フォーマットを使うことができます。

  4. 'f' 、 'd' および 'e' 変換コードについて、パックされた表現は IEEE 754 binary32 ('f' の場合) 、 binary64 ('d' の場合) 、またはbinary16('e' の場合) フォーマットが、プラットフォームにおける浮動小数点数のフォーマットに関係なく使われます。

  5. 'P' フォーマット文字はネイティブバイトオーダでのみ利用可能です (デフォルトの設定を使うか、'@' バイトオーダ指定文字を指定しなければなりません)。 '=' を指定した場合、ホスト計算機のバイトオーダに基づいてリトルエンディアンとビッグエンディアンのどちらを使うかを決めます。 struct モジュールはこの設定をネイティブのオーダ設定として解釈しないので、'P' を使うことはできません。

  6. IEEE 754 の binary16「半精度」型は、IEEE 754 規格 の 2008 年の改訂で導入されました。 符号ビット、5 ビットの指数部、11 ビットの精度 (明示的に格納するのは 10 ビット) を持ち、およそ 6.1e-05 から 6.5e+04 までの数を完全な精度で表せます。 この型は C コンパイラでは広くサポートされていません。 C23 規格の附属書 H をサポートするコンパイラなら、_Float16 型として利用できます。 一般的なマシンでは unsigned short を格納に使えますが、算術演算には使えません。 詳しくは Wikipedia の 半精度浮動小数点数形式 のページを参照してください。

  7. パック時に 'x' は NUL バイトを 1 つ挿入します。

  8. 'p' 書式指定文字は「Pascal 文字列」をエンコードします。 これは、count で指定する 固定バイト数 の領域に格納する短い可変長文字列です。 最初のバイトには、文字列の長さと 255 のうち小さい方を格納します。 その後に文字列のバイト列が続きます。 pack() に渡したバイト列が長すぎる場合 (count から 1 を引いた数より長い場合)、先頭の count-1 バイトだけを格納します。 バイト列が count-1 バイトより短ければ、全体がちょうど count バイトになるようにヌルバイトで埋めます。 unpack() では、'p' 書式指定文字は count バイトを消費しますが、返される bytes オブジェクトの長さは 255 バイトを超えません。 パック時の引数には bytes 型と bytearray 型を使えます。

  9. 's' 書式指定文字では、count は他の書式指定文字のような繰り返し回数ではなく、バイト列の長さとして解釈されます。 例えば '10s' は、Python の 1 つのバイト列に対応する 10 バイトの文字列を意味します。 一方、'10c' は 10 個の Python のバイト列オブジェクトに対応する、別々の 1 バイトの文字要素 10 個 (cccccccccc と同じ) を意味します。 違いを示す具体例は 使用例 を参照してください。 count を指定しなければ、デフォルトは 1 です。 パック時には、指定の長さに合わせてバイト列を切り詰めるかヌルバイトで埋めます。 アンパック時には、結果の bytes オブジェクトは常に指定されたバイト数になります。 特殊な場合として、'0s' は 1 つの空のバイト列を意味します ('0c' は 0 個の文字を意味します)。 パック時の引数には bytes 型と bytearray 型を使えます。

  10. 'Zf' と 'Zd' 型コードでは、プラットフォームで使う浮動小数点数形式にかかわらず、複素数の各成分をそれぞれ IEEE 754 の binary32 と binary64 の形式でパックします。 C では複素数型は任意の機能ですが、ここでは無条件に利用できます。 C11 規格の定義どおり、各複素数型は実部と虚部をこの順で含む 2 要素の C の配列で表します。 互換性のため、'F' と 'D' 書式指定文字もサポートされます (それぞれ 'Zf' と 'Zd' に対応します)。

フォーマット文字の前に整数をつけ、繰り返し回数 (count) を指定することができます。例えば、フォーマット文字列 '4h' は 'hhhh' と全く同じ意味です。

フォーマット文字間の空白文字は無視されます; count とフォーマット文字の間にはスペースを入れてはいけません。

整数フォーマット ('b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q') のいずれかを使って値 x をパックするとき x がフォーマットの適切な値の範囲に無い場合、 struct.error が送出されます。

バージョン 3.1 で変更: 以前は、一部の整数フォーマットでは範囲外の値を範囲内に折り返し、struct.error の代わりに DeprecationWarning を送出していました。

'?' フォーマット文字では、返り値は True または False です。パックするときには、引数オブジェクトの論理値としての値が使われます。 0 または 1 のネイティブや標準の真偽値表現でパックされ、アンパックされるときはゼロでない値は True になります。

使用例

注釈

ネイティブのバイトオーダを使う例 (書式の接頭文字が '@'、または接頭文字がない場合) の結果は、プラットフォームとコンパイラに依存するため、読者のマシンでの結果と一致しないことがあります。

ビッグエンディアンで、3 種類のサイズの整数をパック、アンパックします。

>>> from struct import *
>>> pack(">bhl", 1, 2, 3)
b'\x01\x00\x02\x00\x00\x00\x03'
>>> unpack('>bhl', b'\x01\x00\x02\x00\x00\x00\x03')
(1, 2, 3)
>>> calcsize('>bhl')
7

指定したフィールドには大きすぎる整数をパックしようとする例です。

>>> pack(">h", 99999)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
struct.error: 'h' format requires -32768 <= number <= 32767

's' と 'c' 書式指定文字の違いを示します。

>>> pack("@ccc", b'1', b'2', b'3')
b'123'
>>> pack("@3s", b'123')
b'123'

アンパックした結果のフィールドは、変数に割り当てるか named tuple でラップすることによって名前を付けることができます:

>>> record = b'raymond   \x32\x12\x08\x01\x08'
>>> name, serialnum, school, gradelevel = unpack('<10sHHb', record)

>>> from collections import namedtuple
>>> Student = namedtuple('Student', 'name serialnum school gradelevel')
>>> Student._make(unpack('<10sHHb', record))
Student(name=b'raymond   ', serialnum=4658, school=264, gradelevel=8)

ネイティブモードではパディングが暗黙的に行われるため、書式指定文字の順序がサイズに影響することがあります。 標準モードでは、必要なパディングを利用者が挿入しなければなりません。 以下の最初の pack 呼び出しでは、続く整数を 4 バイト境界にそろえるため、パックされた '#' の後に 3 つの NUL バイトが追加されていることに注目してください。 この例の出力はリトルエンディアンのマシンで生成しました。

>>> pack('@ci', b'#', 0x12131415)
b'#\x00\x00\x00\x15\x14\x13\x12'
>>> pack('@ic', 0x12131415, b'#')
b'\x15\x14\x13\x12#'
>>> calcsize('@ci')
8
>>> calcsize('@ic')
5

以下の書式 'llh0l' では、このプラットフォームの long が 4 バイト境界に配置されるとすると、末尾にパディングバイトが 2 つ追加されます。

>>> pack('@llh0l', 1, 2, 3)
b'\x00\x00\x00\x01\x00\x00\x00\x02\x00\x03\x00\x00'

参考

array モジュール

一様なデータ型からなるバイナリ記録データのパック。

json モジュール

JSON のエンコーダーとデコーダー。

pickle モジュール

Python オブジェクトのシリアライズ。

アプリケーション

struct モジュールの主な用途は 2 つあります。 1 つは、同じアプリケーション内、または同じコンパイラでコンパイルした別のアプリケーションとの間で、Python と C のコードがデータを交換する用途です (ネイティブの形式)。 もう 1 つは、合意したデータ配置を使ってアプリケーション間でデータを交換する用途です (標準の形式)。 一般に、この 2 つの用途では異なる書式文字列を作ります。

ネイティブの形式

ネイティブのメモリ配置を再現する書式文字列を作る場合、バイトオーダとパディングはコンパイラとマシンのアーキテクチャによって決まります。 この場合、@ 書式指定文字でネイティブのバイトオーダとデータサイズを指定してください。 内部のパディングバイトは通常、自動的に挿入されます。 データの塊を続けて配置するときに適切なアラインメントにするには、正しいバイト境界まで切り上げるため、書式文字列の末尾に繰り返し回数がゼロの書式コードが必要になることがあります。

64 ビットのリトルエンディアンのマシンで、次の 2 つの簡単な例を考えます。

>>> calcsize('@lhl')
24
>>> calcsize('@llh')
18

2 番目の書式文字列では、パディングを追加しない限り、データの末尾は 8 バイト境界にそろいません。 繰り返し回数がゼロの書式コードでこの問題を解決できます。

>>> calcsize('@llh0l')
24

'x' 書式コードの繰り返しでも指定できますが、ネイティブの形式では '0l' のような繰り返し回数がゼロの書式を使う方が適切です。

デフォルトではネイティブのバイトオーダとアラインメントを使いますが、接頭文字 '@' で明示する方がよいでしょう。

標準の形式

ネットワークやストレージなど、プロセスの外部とデータをやり取りするときは、バイトオーダ、サイズ、アラインメントを正確に指定してください。 特定のマシンのネイティブの形式と一致するとは仮定しないでください。 例えば、ネットワークバイトオーダはビッグエンディアンですが、多くの普及した CPU はリトルエンディアンです。 これらを明示的に定義すれば、コードの実行環境の詳細を気にせずに済みます。 通常、最初の文字には < または > (あるいは !) を使います。 パディングはプログラマーが行う必要があります。 繰り返し回数がゼロの書式指定文字は役に立ちません。 代わりに、必要な場所に 'x' のパディングバイトを明示的に追加しなければなりません。 前の節の例を見直すと、次のようになります。

>>> calcsize('<qh6xq')
24
>>> pack('<qh6xq', 1, 2, 3) == pack('@lhl', 1, 2, 3)
True
>>> calcsize('@llh')
18
>>> pack('@llh', 1, 2, 3) == pack('<qqh', 1, 2, 3)
True
>>> calcsize('<qqh6x')
24
>>> calcsize('@llh0l')
24
>>> pack('@llh0l', 1, 2, 3) == pack('<qqh6x', 1, 2, 3)
True

上の結果 (64 ビットのマシンで実行) は、別のマシンで実行した場合に一致するとは限りません。 例えば、以下の例は 32 ビットのマシンで実行したものです。

>>> calcsize('<qqh6x')
24
>>> calcsize('@llh0l')
12
>>> pack('@llh0l', 1, 2, 3) == pack('<qqh6x', 1, 2, 3)
False

クラス

struct モジュールは、次の型も定義します:

class struct.Struct(format)

フォーマット文字列 format に従ってバイナリデータを読み書きする、新しい Struct オブジェクトを返します。 Struct オブジェクトを一度作ってからそのメソッドを呼び出すと、フォーマット文字列のコンパイルが一度だけになるので、モジュールレベルの関数を同じフォーマットで呼び出すよりも効率的です。

注釈

モジュールレベルの関数に渡した直近の書式文字列は、コンパイル済みのものがキャッシュされます。 そのため、少数の書式文字列しか使わないプログラムでは、1 つの Struct インスタンスを再利用することを気にする必要はありません。

コンパイルされた Struct オブジェクトは以下のメソッドと属性をサポートします:

pack(v1, v2, ...)

pack() 関数と同じ、コンパイルされたフォーマットを利用するメソッドです。 (len(result) は size と等しいでしょう)

pack_into(buffer, offset, v1, v2, ...)

pack_into() 関数と同じ、コンパイルされたフォーマットを利用するメソッドです。

unpack(buffer)

unpack() 関数と同じ、コンパイルされたフォーマットを利用するメソッドです。 (buffer のバイト数は size と等しくなければなりません)。

unpack_from(buffer, offset=0)

unpack_from() 関数と同じ、コンパイルされたフォーマットを利用するメソッドです。 (offset を始点とする buffer のバイト数は少なくとも size 以上でなければなりません)。

iter_unpack(buffer)

iter_unpack() 関数と同じ、コンパイルされたフォーマットを利用するメソッドです。 (buffer のバイト数は size の倍数でなければなりません)。

Added in version 3.4.

format

この Struct オブジェクトを作成する時に利用されたフォーマット文字列です。

バージョン 3.7 で変更: 書式文字列の型が bytes から str に変更されました。

size

format 属性に対応する構造体の (従って pack() メソッドによって作成されるバイト列オブジェクトの) サイズです。

バージョン 3.13 で変更: 構造体の repr() が変更されました。 現在は次の形式です:

>>> Struct('i')
Struct('i')