ctypes结构体(Structure)通用格式化输出打印等
背景
在使用python
和c\c++
混合编程的时候,我们通常使用python
的ctypes
方案,这时在编码过程中就免不了要与c
的结构体struct
打交道。
在编码过程,尤其是调试中,我们有时需要便捷地查看或者日志打印结构体信息,如果我们按c
的方式一个个结构体成员手工编码输出,这是比较复杂且费力的,因此有必要实现一种通用的结构体格式化输出
的功能,便于查阅结构体对象信息。
本文就是基于上述需求实现的一种方案:定义一个基类,实现对结构体成员变量的格式化输出,其中dump_dict
将结构体转换为字典,__str__
实现对象的字符串类型转换,show
使用``pprint`输出字典数据结果。
实现及代码验证
1.格式化输出结构体信息
定义基类
# 自定义基类结构体
class myStructure(Structure):pass
然后在类myStructure
实现我们的需求,其中dump_dict
为:
# 结构体转字典
def dump_dict(self):info = {}# 通过_fields_获取每一个字段# 检查每个字段的类型,根据不同类型分别处理# 支持递归迭代for k, v in self._fields_:av = getattr(self, k)if type(v) == type(Structure):av = av.dump_dict()elif type(v) == type(Array):av = cast(av, c_char_p).value.decode()else:passinfo[k] = avreturn info
对结构体的字段集合_fields_
遍历,使用getattr
获取对应成员变量的属性值信息,然后根据该信息格式化输出,我们这里格式化到字典类型。
其中对结构体类型
和数组
类型的需要特殊处理,结构体
需要递归调用,数组
则按字符串输出(因在我的实际使用中,数组均为字符串)。
其中__str__
和show
分别基于函数dump_dict
再次封装成我们所需要的功能即可。
# 字符串转换
def __str__(self):info = self.dump_dict()return repr(info)# 打印信息
def show(self):from pprint import pprintpprint(self.dump_dict())
接下来,写代码测试实现效果
# 地址资料
class ST_ADDR(myStructure):_fields_ = [('Addr', c_char*32),('Port', c_int),]# ST_ADDR 测试代码
addr = ST_ADDR()
# 显示:{'Addr': '', 'Port': 0}
addr.show()
# 显示:{'Addr': '', 'Port': 0}
print(addr)# 赋值
addr.Addr = b'127.0.0.1'
addr.Port = 8080
# 显示:{'Addr': '127.0.0.1', 'Port': 8080}
addr.show()
测试发现效果还不错,输出结果较为友好,详见代码注释。
嵌套结构体测试代码:
# 地址资料
class ST_ADDR(myStructure):_fields_ = [('Addr', c_char*32),('Port', c_int),]# HOOK
class ST_HOOK(myStructure):_fields_ = [('UserID', c_uint), # 请求者的ID号码('HostName', c_char * 64), # 主机名('QueueName', c_char * 64), # 队列名('QueueType', c_uint), # 队列类型]# ST_PACKHEAD:包头结构
class ST_PACKHEAD(myStructure):_fields_ = [('RequestType', c_uint), # 请求号码(交易编码)('addr', ST_ADDR), # 请求着的地址(6个子节)('hook', ST_HOOK), # 请求者的私有数据(通讯平台内部使用的)('userdata', c_uint), # 请求者用户数据(应答包会原样返回)('ParmBits',c_ubyte*64), # 包体参数描述]def main():# ST_ADDR 测试代码addr = ST_ADDR()# 显示:{'Addr': '', 'Port': 0}addr.show()# 显示:{'Addr': '', 'Port': 0}print(addr)# 赋值addr.Addr = b'127.0.0.1'addr.Port = 8080# 显示:{'Addr': '127.0.0.1', 'Port': 8080}addr.show()# ST_HOOK 测试代码hook = ST_HOOK()# 显示:{'HostName': '', 'QueueName': '', 'QueueType': 0, 'UserID': 0}hook.show()# 显示:{'UserID': 0, 'HostName': '', 'QueueName': '', 'QueueType': 0}print(hook)# 赋值hook.UserID = 578hook.HostName = b'127.0.0.1'hook.QueueName = b'q_req'hook.QueueType = 5205# 显示:{'UserID': 578, 'HostName': '127.0.0.1', 'QueueName': 'q_req', 'QueueType': 5205}print(hook)# ST_PACKHEAD 复杂结构体,嵌套结构体head = ST_PACKHEAD()# 显示内容如下:# 'ParmBits': '',# 'RequestType': 0,# 'addr': {'Addr': '', 'Port': 0},# 'hook': {'HostName': '', 'QueueName': '', 'QueueType': 0, 'UserID': 0},# 'userdata': 0}head.show()# 显示:{'RequestType': 0, 'addr': {'Addr': '', 'Port': 0}, 'hook': {'UserID': 0, 'HostName': '', 'QueueName': '', 'QueueType': 0}, 'userdata': 0, 'ParmBits': ''}print(head)# 赋值head.RequestType = 404456head.userdata = 1234head.addr.Addr = b'127.0.0.1'head.hook = hook# 显示内容如下:# {'ParmBits': '',# 'RequestType': 404456,# 'addr': {'Addr': '127.0.0.1', 'Port': 0},# 'hook': {'HostName': '127.0.0.1',# 'QueueName': 'q_req',# 'QueueType': 5205,# 'UserID': 578},# 'userdata': 1234}head.show()
发现嵌套后的复杂结构体依旧可以正常输出,|゚ρ゚ )ノ哦哟!
work over~~~
2.内存地址操作,字符串内存块输出
直接将结构体转为内存卡地址,并将其数据输出,实现代码为:
# 字符串信息
def string(self):return string_at(addressof(self), sizeof(self))
测试代码:
# ST_ADDR 测试代码
addr = ST_ADDR()
addr.Addr = b'127.0.0.1'
addr.Port = 8080
print(addr.string())
输出结果为:
b'127.0.0.1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x90\x1f\x00\x00'
3.内存地址操作,二进制格式化输出
对指定内存块的数据,以统一的数据数据类型解析输出,一般用于输出纯字符串或其他简单类型的数组,实现代码为:
# 按表格输出二进制数据
# 参数 ctp : 每一列的数据类型,其类型为ctypes数据类型,如c_char, c_int
# 参数 row_num : 每一行的列数
# 参数 full : 是否完整显示数据,如果是且结构体大小不是ctp大小的整数倍,则将ctp强制转为c_char类型
def show_meminfo(self, ctp=c_char, row_num = 8, full = False):import struct# # 长度不够时,取char类型if full and sizeof(self)%sizeof(ctp):ctp = c_charblock = int(sizeof(self)/sizeof(ctp))addr = string_at(addressof(self), sizeof(self))for i in range(block):v = struct.unpack(ctp._type_, addr[i*sizeof(ctp):(i+1)*sizeof(ctp)])pend = '\n' if (i+1)%row_num==0 else ' 'print(repr(v[0]), end=pend)print()
测试代码为:
class ST_DATA(myStructure):_fields_ = [('day1', c_int*3),('day2', c_int * 4),('day3', c_int * 5),('day4', c_int * 6),('day5', c_int * 7),]def main():# ST_DATA 测试代码data = ST_DATA()data.day1 = (c_int*3)(11, 12, 13)data.day2 = (c_int * 4)(21, 22, 23, 24)data.day3 = (c_int * 5)(31, 32, 33, 34, 35)data.day4 = (c_int * 6)(41, 42, 43, 44, 45, 46)data.day5 = (c_int * 7)(51, 52, 53, 54, 55, 56, 57)data.show_meminfo(c_int, 4)
其输出结果为:
11 12 13 21
22 23 24 31
32 33 34 35
41 42 43 44
45 46 51 52
53 54 55 56
57
可以发现输出结果中数据按c_int
单元解析,并对齐输出
完整代码及测试用例
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from ctypes import *# 自定义基类结构体
class myStructure(Structure):# 结构体转字典def dump_dict(self):info = {}# 通过_fields_获取每一个字段# 检查每个字段的类型,根据不同类型分别处理# 支持递归迭代for k, v in self._fields_:av = getattr(self, k)if type(v) == type(Structure):av = av.dump_dict()elif type(v) == type(Array):av = cast(av, c_char_p).value.decode()else:passinfo[k] = avreturn info# 字符串转换def __str__(self):info = self.dump_dict()return repr(info)# 打印信息def show(self):from pprint import pprintpprint(self.dump_dict())# 地址资料
class ST_ADDR(myStructure):_fields_ = [('Addr', c_char*32),('Port', c_int),]# HOOK
class ST_HOOK(myStructure):_fields_ = [('UserID', c_uint), # 请求者的ID号码('HostName', c_char * 64), # 主机名('QueueName', c_char * 64), # 队列名('QueueType', c_uint), # 队列类型]# ST_PACKHEAD:包头结构
class ST_PACKHEAD(myStructure):_fields_ = [('RequestType', c_uint), # 请求号码(交易编码)('addr', ST_ADDR), # 请求着的地址(6个子节)('hook', ST_HOOK), # 请求者的私有数据(通讯平台内部使用的)('userdata', c_uint), # 请求者用户数据(应答包会原样返回)('ParmBits',c_ubyte*64), # 包体参数描述]def main():# ST_ADDR 测试代码addr = ST_ADDR()# 显示:{'Addr': '', 'Port': 0}addr.show()# 显示:{'Addr': '', 'Port': 0}print(addr)# 赋值addr.Addr = b'127.0.0.1'addr.Port = 8080# 显示:{'Addr': '127.0.0.1', 'Port': 8080}addr.show()# ST_HOOK 测试代码hook = ST_HOOK()# 显示:{'HostName': '', 'QueueName': '', 'QueueType': 0, 'UserID': 0}hook.show()# 显示:{'UserID': 0, 'HostName': '', 'QueueName': '', 'QueueType': 0}print(hook)# 赋值hook.UserID = 578hook.HostName = b'127.0.0.1'hook.QueueName = b'q_req'hook.QueueType = 5205# 显示:{'UserID': 578, 'HostName': '127.0.0.1', 'QueueName': 'q_req', 'QueueType': 5205}print(hook)# ST_PACKHEAD 复杂结构体,嵌套结构体head = ST_PACKHEAD()# 显示内容如下:# 'ParmBits': '',# 'RequestType': 0,# 'addr': {'Addr': '', 'Port': 0},# 'hook': {'HostName': '', 'QueueName': '', 'QueueType': 0, 'UserID': 0},# 'userdata': 0}head.show()# 显示:{'RequestType': 0, 'addr': {'Addr': '', 'Port': 0}, 'hook': {'UserID': 0, 'HostName': '', 'QueueName': '', 'QueueType': 0}, 'userdata': 0, 'ParmBits': ''}print(head)# 赋值head.RequestType = 404456head.userdata = 1234head.addr.Addr = b'127.0.0.1'head.hook = hook# 显示内容如下:# {'ParmBits': '',# 'RequestType': 404456,# 'addr': {'Addr': '127.0.0.1', 'Port': 0},# 'hook': {'HostName': '127.0.0.1',# 'QueueName': 'q_req',# 'QueueType': 5205,# 'UserID': 578},# 'userdata': 1234}head.show()if __name__ == '__main__':main()
ctypes结构体(Structure)通用格式化输出打印等相关推荐
- 编写C语言代码,实现以下功能:有N名学生,每个人的信息包括学号、姓名和语文、数学、英语、平均分等内容(用结构体表示 ),输出平均分排名倒数第2的学生信息。
编写C语言代码,实现以下功能: 有N名学生,每个人的信息包括学号.姓名和语文.数学.英语.平均分等内容(用结构体表示 ),输出平均分排名倒数第2的学生信息. #define _CRT_SECURE_N ...
- php变量原格式输出,PHP格式化输出打印变量
PHP 常用的输入变量函数print_r,但是输出没有换行,看起来很费力 我们可以自定义一个函数来实现变量的格式化输出,代码如下: function dump($vars, $label = '', ...
- 定义一个结构体student,存储学生的学号、名字、性别和年龄,读入每个学生的所有信息,保存在结构体中,并输出。
题目描述 定义一个结构体student,存储学生的学号.名字.性别和年龄,读入每个学生的所有信息,保存在结构体中,并输出.结构体student的定义如下: struct student { int n ...
- Python Ctypes结构体指针处理(函数参数,函数返回)
参考网址: http://www.2cto.com/kf/201109/106444.html 本文演示了在python中调用C语言生成的动态库,返回结构体指针,并进行输出! test.c(动态库源代 ...
- 结构体structure
结构体是值类型 import Foundation struct TV{ var keyName="a" var keyNumber=9 func getKey()->Int ...
- c语言结构体赋值,并输出各种类型变量的值
1 代码 #include<stdio.h> struct Student{ char sex; int age; char name[10]; }; main(){ st ...
- Golang实践录:利用反射reflect构建通用打印结构体接口
本文针对 Golang 的结构体字段的打印进行一些研究.其中涉及到一些反射的知识. 问题提出 总结一些实践情况,结构体字段值的输出还是比较常见的,至少笔者目前常用.比如输出某些数据表的数据(代码中会转 ...
- python高级ctypes数据类型—结构体
结构体在ctypes中通过类进行定义.用于定义结构体的类需要继承自ctypes的Structure基类,而后通过定义类的_fields_属性来定义结构体的构成._fields_属性一般定义为一个二维的 ...
- FFMpeg4.0相关结构体和函数
文章目录 相关指令 相关结构体 av_register_all() 已废弃无需添加 avformat_network_init() 初始化网络封装库 AVFormatContext结构体 AVDict ...
最新文章
- VMware下ghost安装XP后无法从硬盘启动的问题
- 操作系统常用调度算法
- gdb加载python_gdb加载python脚本的方法
- Linux下载交通图片数据集CityScapes Dataset
- EJS学习(一)之特性、安装、工作原理
- Scanf连续调用多次并且存在%c的问题
- Discuz!NT 和网站整合
- centos7 安装mysql_Centos7安装最新版本的MySQL
- PHP学习记录_基本语法
- java开发微信抢红包挂_「高并发秒杀」微信抢红包实战案例
- Linux学习笔记第八周七次课(4月3日)
- mate7 刷机 android 7,Mate7 四大版本完整稳定版刷机包大集合!
- matlab单项pwm整流电流内环pr控制仿真,基于PR的单相PWM整流器电流控制研究
- 声音莫名从扬声器切换到听筒_扬声器听筒的切换
- No module named ‘frontend‘
- 计算机视觉论文-2021-07-15
- F1赛车相关公开数据集
- Java实现云端存储、短信、邮件、沙盒支付
- MyBatis入门回顾
- 怎么从安卓设备转移数据到苹果_换手机了数据怎么办?这样做安卓、苹果手机数据一键转移...
热门文章
- WEB漏洞—文件包含漏洞
- 新版福昕阅读器(Foxit Reader)启动速度慢解决办法
- Day 86/100 手机的XYZ轴和地球的XYZ轴
- 47.serch基本语法
- PatchMatch Stereo - Stereo Matching with Slanted Support Windows
- uboot代码解析4:内核传参、ramdisk、initrd
- matplotlib绘制柱状图之基本配置——万能模板案例
- 网络基础(二)- TCP协议
- Springboot 系列(十七)迅速使用 Spring Boot Admin 监控你的 Spring Boot 程序,支持异常邮件通知
- 大数据旅游:智慧旅游的先锋