本日はPythonのctypesに関する記事です。
実用的な内容ですので是非LGTM ストックお願いします ^__^
ctypesとは
C言語のデータ構造をPythonで扱うことができるライブラリです。
Pythonをインストールするときにこちらのライブラリは標準でインストールされます。
Cの構造体をPythonでも扱いたいときなどに非常に便利です。
事例
今回紹介する例としては、
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 初版