Pythonで構造体を扱う方法を解説する

スポンサーリンク

Pythonのstructモジュールは、C言語のstructureデータ型にアクセスし、操作するためのシンプルなPythonicインタフェースを提供するために使用されています。

これは、C言語のコードを扱う必要があり、C言語は低レベル言語なのでC言語でツールを書く時間がない場合に便利なツールになりえます。

このモジュールは Python の値を C の構造体に変換し、その逆も可能です。

Cではオブジェクトと呼ばれるものはなく、バイトサイズのデータ構造のみなので、Cの構造体はPythonのバイトオブジェクトとして使用されます。

このモジュールを使って、C言語の構造体に対するPythonのインタフェースをどのように持つことができるかを理解しましょう。


スポンサーリンク

Python struct Module Methods

このモジュールでは、C言語の構造体を扱いますので、このモジュールが提供する関数のいくつかを見てみましょう。

struct.pack()

これは、Pythonのバイト文字列(バイトオブジェクト)に要素をパックするために使用されます。

Pythonのストレージはバイトで構成されているため、C言語のプログラムでもPythonの pack() の出力を利用することができます。

フォーマット: struct.pack(format, v1, v2, …)

v1,v2, ... は、バイトオブジェクトにパックされる値です。

これらは、C 言語の構造体のフィールドの値を表します。

n 個のフィールドを持つ C 言語の構造体は n 個の値を正確に持たなければならないので、引数はフォーマットが要求する値と正確に一致しなければなりません。

ここで、format とは、パッキング時のフォーマットを意味します。

これは、Cのコードで使われるようなバイト文字列のデータ型を指定する必要があるため、必要なのです。

下の表は、format に指定する最も一般的な値の一覧です。

データ型を指定するために、1つの値につき1つのフォーマットが必要です。

| フォーマット|Cデータ型|Python型||||。

| c| char|長さ1の文字列||。

| ? | _Bool | bool|です。

| h | short | integer|整数型。

| l| long| integer|(整数値)。

| i| int| integer|(整数)|。

| f|float|フロート|float
| d
| ダブル | フロート
| s`| char[] | 文字列|の例です。

いくつかの例を使って理解していきましょう。

以下のスニペットは pack() を使って、3つの整数 1, 2, 3 をバイトオブジェクトに格納しています。

私のマシンでは整数のサイズは4バイトなので、4バイトのブロックが3つ見えますが、これはC言語の3つの整数に相当します。

import struct
 
# We pack 3 integers, so 'iii' is required
variable = struct.pack('iii', 1, 2, 3)
print(type(variable), variable)
 
variable_2 = struct.pack('iic', 1, 2, b'A')
print('
'
, variable_2)

結果は以下の通りです。

<class 'bytes'> b'x01x00x00x00x02x00x00x00x03x00x00x00'
b'x01x00x00x00x02x00x00x00A'

適切な型が渡されないと、Python の struct モジュールによって struct.error という例外が発生します。

import struct
 
# Error!! Incorrect datatype assignment
variable = struct.pack('ccc', 1, 2, 3)
print(type(variable), variable)

結果は以下の通りです。

struct.error: char format requires a bytes object of length 1

struct.unpack()

Python structモジュールのこの関数は、パックされた値を適切なフォーマットに従って元の表現に解凍します。

これは、バイトオブジェクトが要素を与えるために解凍されるので、渡された値の数に等しいサイズのタプルを返します。

フォーマット: struct.unpack(format, string)

これは、バイト stringformat フォーマット指定子に従って解凍します。

これは、 struct.pack() の逆です。

この関数で生成した古いバイト列を、 unpack() を使って Python に渡された値を取り戻してみましょう。

import struct
 
byte_str = b'x01x00x00x00x02x00x00x00A'
 
# Using the same format specifier as before, since
# we want to get Python values for the same byte-string
tuple_vals = struct.unpack('iic', byte_str)
print(tuple_vals)

結果は以下の通りです。

(1, 2, b'A')

ご覧の通り、pack()unpack() の両方で同じフォーマット指定子を使えば、確かにこのタプルから古いPythonの値をpackすることができます。


struct.calcsize()

この関数は、与えられた書式指定子を使用して、データの型を取得し、サイズを計算するために、構造体のString表現の総サイズを返します。

書式: struct.calcsize(fmt)

import struct
 
print('C Integer Size in Bytes:', struct.calcsize('i'))
print('Size of 3 characters in Bytes:', struct.calcsize('ccc'))

結果は以下の通りです。

C Integer Size in Bytes: 4
Size of 3 characters in Bytes: 3

struct.pack_into()

この関数は、Pythonの文字列バッファに値を格納するために使用され、 ctypesモジュールで利用可能です。

フォーマット: struct.pack_into(fmt, buffer, offset, v1, v2, …)

ここで、fmt はいつものようにフォーマット指定子を指します。

bufferは、パックされた値を格納する文字列バッファを指定します。

また、ベースアドレスからのoffset` 位置を指定して、そこからパッキングを行うこともできます。

これは何の値も返さず、単に buffer 文字列に値を格納するだけです。

import struct
import ctypes
 
# We will create a string buffer having a size
# equal to that of a struct with 'iic' values.
buf_size = struct.calcsize('iic')
 
# Create the string buffer
buff = ctypes.create_string_buffer(buf_size)
   
# struct.pack() returns the packed data
struct.pack_into('iic', buff, 0, 1, 2, b'A')
 
print(buff)
 
# Display the contents of the buffer
print(buff[:])

結果は以下の通りです。

<ctypes.c_char_Array_9 object at 0x7f4bccef1040>
b'x01x00x00x00x02x00x00x00A'

実際に、バッファの文字列にパックされた値を得ることができます。


struct.unpack_from()

unpack()と同様に、バッファの文字列から値を展開するための対応するものが存在します。

これはstruct.pack_into()` の逆を行います。

フォーマット: struct.unpack_from(fmt, buffer, offset)

struct.unpack()`と同様に、値のタプルを返します。

import struct
import ctypes
 
# We will create a string buffer having a size
# equal to that of a struct with 'iic' values.
buf_size = struct.calcsize('iic')
 
# Create the string buffer
buff = ctypes.create_string_buffer(buf_size)
   
# struct.pack() returns the packed data
struct.pack_into('iic', buff, 0, 1, 2, b'A')
 
print(struct.unpack_from('iic', buff, 0))

結果は以下の通りです。

(1, 2, b'A')

まとめ

今回は、Pythonのstructモジュールを使って、C言語の構造体オブジェクトを扱う方法を学びました。

タイトルとURLをコピーしました