背景

在使用pythonc\c++混合编程的时候,我们通常使用pythonctypes方案,这时在编码过程中就免不了要与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)通用格式化输出打印等相关推荐

  1. 编写C语言代码,实现以下功能:有N名学生,每个人的信息包括学号、姓名和语文、数学、英语、平均分等内容(用结构体表示 ),输出平均分排名倒数第2的学生信息。

    编写C语言代码,实现以下功能: 有N名学生,每个人的信息包括学号.姓名和语文.数学.英语.平均分等内容(用结构体表示 ),输出平均分排名倒数第2的学生信息. #define _CRT_SECURE_N ...

  2. php变量原格式输出,PHP格式化输出打印变量

    PHP 常用的输入变量函数print_r,但是输出没有换行,看起来很费力 我们可以自定义一个函数来实现变量的格式化输出,代码如下: function dump($vars, $label = '', ...

  3. 定义一个结构体student,存储学生的学号、名字、性别和年龄,读入每个学生的所有信息,保存在结构体中,并输出。

    题目描述 定义一个结构体student,存储学生的学号.名字.性别和年龄,读入每个学生的所有信息,保存在结构体中,并输出.结构体student的定义如下: struct student { int n ...

  4. Python Ctypes结构体指针处理(函数参数,函数返回)

    参考网址: http://www.2cto.com/kf/201109/106444.html 本文演示了在python中调用C语言生成的动态库,返回结构体指针,并进行输出! test.c(动态库源代 ...

  5. 结构体structure

    结构体是值类型 import Foundation struct TV{ var keyName="a" var keyNumber=9 func getKey()->Int ...

  6. c语言结构体赋值,并输出各种类型变量的值

    1 代码 #include<stdio.h> struct Student{   char sex;   int age;   char name[10]; }; main(){   st ...

  7. Golang实践录:利用反射reflect构建通用打印结构体接口

    本文针对 Golang 的结构体字段的打印进行一些研究.其中涉及到一些反射的知识. 问题提出 总结一些实践情况,结构体字段值的输出还是比较常见的,至少笔者目前常用.比如输出某些数据表的数据(代码中会转 ...

  8. python高级ctypes数据类型—结构体

    结构体在ctypes中通过类进行定义.用于定义结构体的类需要继承自ctypes的Structure基类,而后通过定义类的_fields_属性来定义结构体的构成._fields_属性一般定义为一个二维的 ...

  9. FFMpeg4.0相关结构体和函数

    文章目录 相关指令 相关结构体 av_register_all() 已废弃无需添加 avformat_network_init() 初始化网络封装库 AVFormatContext结构体 AVDict ...

最新文章

  1. VMware下ghost安装XP后无法从硬盘启动的问题
  2. 操作系统常用调度算法
  3. gdb加载python_gdb加载python脚本的方法
  4. Linux下载交通图片数据集CityScapes Dataset
  5. EJS学习(一)之特性、安装、工作原理
  6. Scanf连续调用多次并且存在%c的问题
  7. Discuz!NT 和网站整合
  8. centos7 安装mysql_Centos7安装最新版本的MySQL
  9. PHP学习记录_基本语法
  10. java开发微信抢红包挂_「高并发秒杀」微信抢红包实战案例
  11. Linux学习笔记第八周七次课(4月3日)
  12. mate7 刷机 android 7,Mate7 四大版本完整稳定版刷机包大集合!
  13. matlab单项pwm整流电流内环pr控制仿真,基于PR的单相PWM整流器电流控制研究
  14. 声音莫名从扬声器切换到听筒_扬声器听筒的切换
  15. No module named ‘frontend‘
  16. 计算机视觉论文-2021-07-15
  17. F1赛车相关公开数据集
  18. Java实现云端存储、短信、邮件、沙盒支付
  19. MyBatis入门回顾
  20. 怎么从安卓设备转移数据到苹果_换手机了数据怎么办?这样做安卓、苹果手机数据一键转移...

热门文章

  1. WEB漏洞—文件包含漏洞
  2. 新版福昕阅读器(Foxit Reader)启动速度慢解决办法
  3. Day 86/100 手机的XYZ轴和地球的XYZ轴
  4. 47.serch基本语法
  5. PatchMatch Stereo - Stereo Matching with Slanted Support Windows
  6. uboot代码解析4:内核传参、ramdisk、initrd
  7. matplotlib绘制柱状图之基本配置——万能模板案例
  8. 网络基础(二)- TCP协议
  9. Springboot 系列(十七)迅速使用 Spring Boot Admin 监控你的 Spring Boot 程序,支持异常邮件通知
  10. 大数据旅游:智慧旅游的先锋