5
4

More than 1 year has passed since last update.

Python ctypes 構造体 pragma pack (push,1)

Last updated at Posted at 2021-11-18

本日はPythonのctypesに関する記事です。
実用的な内容ですので是非LGTM ストックお願いします ^__^

ctypesとは

C言語のデータ構造をPythonで扱うことができるライブラリです。
Pythonをインストールするときにこちらのライブラリは標準でインストールされます。

Cの構造体をPythonでも扱いたいときなどに非常に便利です。

Python ctypes 公式ドキュメント

事例

今回紹介する例としては、

C側:構造体のデータをバイナリでログとして出力
Python:C側で吐き出されたバイナリのログデータを読み込んで表示

PythonでCのログを解析することで2次利用(可視化や機械学習)が各段にしやすいのでマスターしたい技術です。

pragma pack (push, 1)

C側では構造体を定義するとき、 pragma pack をよく利用します。
これをPythonで定義するためには、下記が必要なので忘れないでください。

他のサイトにあまり書かれていないので、ここに書いておきます。

import ctypes

class ST_DATA(ctypes.Structure):
    _pack_ = 1 # これが必須!!!
    _fields_ = (
        # 構造体データを自由に定義
    )

それではここから事例の内容をコードで紹介します。

C側

かなり省略して書きますのでご了承ください。構造体の部分のみ抜き出し。

#pragma pack (push,1)

typedef struct {
    long ID;
    short result;
    double data[10];
} ST_DATA

Python側

import ctypes
import os

class ST_DATA(ctypes.Structure):
    _pack_ = 1 # pragma pack (push, 1) と同等. これ入れないとデータがパディングされてしまう。
    _fields_ = (
        ('ID', ctypes.c_long),
        ('result', ctypes.c_short),
        ('data', ctypes.c_double*10),
    )

dataList = []

def ReadData(logFilePath:str):
    global dataList

    if os.path.exists(logFilePath):
        with open(logFilePath, 'rb') as f:
            readBytes = bytearray(f.read())
            print(f"readBytes len: {len(readBytes)}")

            logCnt = len(readBytes) / ctypes.sizeof(ST_DATA) # 構造体データが何個存在するか算出
            print(f"logCnt:{logCnt}") 

            # 読みだしたデータから各構造体のデータに分割
            for no in range(logCnt):
                bytesArr = readBytes[no*ctypes.sizeof(ST_DATA) : (no+1)*ctypes.sizeof(ST_DATA)]
                datum = ST_DATA.from_buffer(bytesArr) # binaryデータを構造体データに変換
                dataList.append(datum)
    else:
        print("log is not exist")

if __name__ == "__main__":
    filename = "log.bin" # 構造体データがいくつも書かれたバイナリファイル

    # ログデータを読み込む
    ReadData(filename)

    # データの取得方法 例
    print(dataList[0].ID) # 1番最初のログのIDを表示
    print(dataList[0].result) # 1番最初のログのresultを表示
    print(dataList[0].data[0]) # 1番最初のログのdataの1要素目を表示
    print(dataList[0].data[9]) # 1番最初のログのdataの10要素目を表示

Python ctypes Tips

構造体のサイズを取得

print(ctypes.sizeof(ST_DATA))

byte配列から構造体データに変換

# bytesTmpにはbyte配列を準備
stData = ST_DATA.from_buffer(bytesTmp)

構造体のフィールドを全て取得

print(ST_DATA._fields_)

以上で本日の記事は終了です。
是非参考にしてください!

更新履歴

  • 2021/11/18 初版
5
4
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
5
4