字节序转换 + 模板 这里有一个结构体:// 类型别名对齐协议 typedef quint8 uchar; typedef qint8 schar; typedef quint16 ushort; typedef qint16 sshort; typedef quint32 uint; typedef qint32 sint; #pragma pack (push,1) // 报文头 typedef struct tagHead { unsigned char m_Iofo; unsigned short m_MesLength; unsigned short m_Send; unsigned short m_Rece; unsigned char m_TaskAttr; unsigned char m_CommType; unsigned int m_Serial; void init(quint8 Iden, quint16 len, quint16 sender, quint16 recv, quint8 task, quint8 label) { m_Iofo Iden; m_MesLength len; m_Send sender; m_Rece recv; m_TaskAttr task; m_CommType label; } }HEAD; typedef struct tagMainCtrlBitReplyMes { HEAD m_head; unsigned char m_MainCtrl; unsigned char m_Ser; unsigned char m_CheckSum; // 校验和 }MainCtrlBitReplyMes; #pragma pack (pop)在网络通信中因为我和对方的电脑都是小端序之前我们直接通过内存映射来将这段信息转为字节流。MultiDataMes msg; QByteArray ba((const char *)msg,sizeof(tagMultiDataMes));但现在要求网络传输使用大端序这里就涉及到字节序转换了。小端 --- 大端(网络字节序) --- 小端先实现小端 --- 大端(网络字节序)我们的代码涉及到需要转换的基础数据类型:int,unsigned intshort,unsigned short#include QByteArray #include QDataStream QByteArray HeadToNet(const HEAD head) { QByteArray buf; QDataStream ds(buf, QIODevice::WriteOnly); // 强制网络大端序 ds.setByteOrder(QDataStream::BigEndian); // 按结构体顺序依次写入 ds head.m_Iofo; ds head.m_MesLength; ds head.m_Send; ds head.m_Rece; ds head.m_TaskAttr; ds head.m_CommType; ds head.m_Serial; return buf; }上面的代码把结构体Head对象以大端序转为字节流存储QByteArray MainCtrlReplyToNet(const MainCtrlBitReplyMes msg) { QByteArray buf; QDataStream ds(buf, QIODevice::WriteOnly); ds.setByteOrder(QDataStream::BigEndian); // 1. 先写入报文头全部字段 QByteArray headBuf HeadToNet(msg.m_head); ds.writeRawData(headBuf.constData(), headBuf.size()); // 2. 写入报文体 ds msg.m_MainCtrl; ds msg.m_Ser; ds msg.m_CheckSum; return buf; }我们的结构体是两层嵌套的上面的代码把结构体MainCtrlReplyToNet对象以大端序转为字节流存储QByteArray headBuf HeadToNet(msg.m_head); ds.writeRawData(headBuf.constData(), headBuf.size());这里需要使用writeRawData函数不可以直接写成ds headBuf;这里headBuf为: 9e000f02011001a12000000000但是 ds前面多出来0000000d原因:QDataStream 序列化 QByteArray 时默认先写入 4 字节长度头大端 int再写数组内容你看到前面多的0000000d就是长度字段0x0d 13HEAD 刚好 13 字节。大端序 --- 结构体依然使用QDataStream// 返回true解析成功false数据长度不足/解析失败 bool NetToHead(QDataStream ds, HEAD outHead) { // 逐个读取字段 ds outHead.m_Iofo; ds outHead.m_MesLength; ds outHead.m_Send; ds outHead.m_Rece; ds outHead.m_TaskAttr; ds outHead.m_CommType; ds outHead.m_Serial; // 校验流状态读取出错返回false return ds.status() QDataStream::Ok; }bool NetToMainCtrlReply(const QByteArray netData, MainCtrlBitReplyMes outMsg) { QDataStream ds(netData); ds.setByteOrder(QDataStream::BigEndian); // 读取HEAD头 NetToHead(ds,outMsg.m_head); // 读取应答数据域 ds outMsg.m_MainCtrl; ds outMsg.m_Ser; ds outMsg.m_CheckSum; return ds.status() QDataStream::Ok; }实际上关键就是QDataStream我们使用它进行 结构体 --- 大端序字节流 大端序字节流 --- 结构体进一步来讲我的软件涉及到可能30个类似的报文我不想手动一个个去写我希望可以自动化一些。例// 混合数值报文示例包含全部 short/ushort/int/uint/char/uchar typedef struct tagMultiDataMes { HEAD m_head; // 单字节 schar m_charVal; uchar m_ucharVal; // 两字节 short sshort m_shortVal; ushort m_ushortVal; // 四字节 int sint m_intVal; uint m_uintVal; // 末尾校验字节和你原有报文风格统一 uchar m_CheckSum; } MultiDataMes;自动化思路:实际上对于每个结构体来说Head部分都是相同的代码我们只需要特化数据部分。HEAD相关函数:#include QByteArray #include QDataStream QByteArray HeadToNet(const HEAD head) { QByteArray buf; QDataStream ds(buf, QIODevice::WriteOnly); // 强制网络大端序 ds.setByteOrder(QDataStream::BigEndian); // 按结构体顺序依次写入 ds head.m_Iofo; ds head.m_MesLength; ds head.m_Send; ds head.m_Rece; ds head.m_TaskAttr; ds head.m_CommType; ds head.m_SerialNum; return buf; } // 返回true解析成功false数据长度不足/解析失败 bool NetToHead(QDataStream ds, HEAD outHead) { // 逐个读取字段 ds outHead.m_Iofo; ds outHead.m_MesLength; ds outHead.m_Send; ds outHead.m_Rece; ds outHead.m_TaskAttr; ds outHead.m_CommType; ds outHead.m_SerialNum; // 校验流状态读取出错返回false return ds.status() QDataStream::Ok; } QDataStream operator(QDataStream ds, const HEAD head) { QByteArray msg HeadToNet(head); ds.writeRawData(msg.constData(), msg.size()); return ds; } QDataStream operator(QDataStream ds, HEAD head) { NetToHead(ds,head); return ds; }HEAD配套完整 QDataStream 重载这时候可以优化一下之前的代码:QByteArray MainCtrlReplyToNet(const MainCtrlBitReplyMes msg) { QByteArray buf; QDataStream ds(buf, QIODevice::WriteOnly); ds.setByteOrder(QDataStream::BigEndian); // 1. 先写入报文头全部字段 ds msg.m_head; // 2. 写入报文体 ds msg.m_MainCtrl; ds msg.m_Ser; ds msg.m_CheckSum; return buf; } bool NetToMainCtrlReply(const QByteArray netData, MainCtrlBitReplyMes outMsg) { QDataStream ds(netData); ds.setByteOrder(QDataStream::BigEndian); // 读取HEAD头 ds outMsg.m_head; // 读取应答数据域 ds outMsg.m_MainCtrl; ds outMsg.m_Ser; ds outMsg.m_CheckSum; return ds.status() QDataStream::Ok; }注意我想使用脚本来完成这部分工作输入是我们的结构体输出是下面的文本:QDataStream operator(QDataStream ds, const MainCtrlBitReplyMes msg) { ds msg.m_head; ds msg.m_MainCtrl; ds msg.m_Ser; ds msg.m_CheckSum; return ds; } QDataStream operator(QDataStream ds, MainCtrlBitReplyMes msg) { ds msg.m_head; ds msg.m_MainCtrl; ds msg.m_Ser; ds msg.m_CheckSum; return ds; }那么代码可以进一步优化:QByteArray MainCtrlReplyToNet(const MainCtrlBitReplyMes msg) { QByteArray buf; QDataStream ds(buf, QIODevice::WriteOnly); ds.setByteOrder(QDataStream::BigEndian); ds msg; return buf; } bool NetToMainCtrlReply(const QByteArray netData, MainCtrlBitReplyMes outMsg) { QDataStream ds(netData); ds.setByteOrder(QDataStream::BigEndian); // 读取HEAD头 ds outMsg; return ds.status() QDataStream::Ok; }再添加一个结构体:typedef struct tagTestDataMes { HEAD m_head; int m_i32; unsigned int m_ui32; short m_i16; unsigned short m_ui16; char m_c8; unsigned char m_uc8; }TestDataMes; // char 输出重载 QDataStream operator(QDataStream ds, char c) { ds static_castqint8(c); return ds; } // char 输入重载 QDataStream operator(QDataStream ds, char c) { qint8 tmp; ds tmp; c static_castchar(tmp); return ds; } // TestDataMes serialize QDataStream operator(QDataStream ds, const TestDataMes msg) { ds msg.m_head; ds msg.m_i32; ds msg.m_ui32; ds msg.m_i16; ds msg.m_ui16; ds msg.m_c8; ds msg.m_uc8; return ds; } // TestDataMes deserialize QDataStream operator(QDataStream ds, TestDataMes msg) { ds msg.m_head; ds msg.m_i32; ds msg.m_ui32; ds msg.m_i16; ds msg.m_ui16; ds msg.m_c8; ds msg.m_uc8; return ds; }需要注意的是需要重载char类型QDataStream的和只支持 Qt 标准整数类型qint8/quint8/qint16/quint16/qint32/quint32代码里直接用了原生charQDataStream没有重载char编译直接报二元运算符错误。所以我们只需要生成结构体对应的QDataStream 重载函数。我们再继续优化编写模板函数:#include QByteArray #include QDataStream // 通用结构体 → 网络字节数组 templatetypename T QByteArray StructToNet(const T msg) { QByteArray buf; QDataStream ds(buf, QIODevice::WriteOnly); ds.setByteOrder(QDataStream::BigEndian); ds msg; return buf; } // 通用网络字节数组 → 结构体 templatetypename T bool NetToStruct(const QByteArray netData, T outMsg) { QDataStream ds(netData); ds.setByteOrder(QDataStream::BigEndian); ds outMsg; return ds.status() QDataStream::Ok; }测试程序1:int main(int argc, char *argv[]) { QApplication a(argc, argv); MainCtrlBitReplyMes msg; memset(msg,0,sizeof(msg)); msg.m_head.init(0x9e,sizeof(MainCtrlBitReplyMes) - 1,0x0201,0x1001,0xA1,0x20); msg.m_MainCtrl 0x01; msg.m_Ser 0x10; msg.m_CheckSum 0x20; QByteArray ba; ba.append((const char *)msg,sizeof(msg)); qDebug() ba.toHex(); QByteArray changed StructToNet(msg); qDebug() changed.toHex(); MainCtrlBitReplyMes msg1; NetToStruct(changed,msg1); { QByteArray ba; ba.append((const char *)msg1,sizeof(msg1)); qDebug() ba.toHex(); qDebug() __FILE__ __LINE__ msg1.m_Ser; } return a.exec(); }9e0f0001020110a120000000000110209e000f02011001a120000000000110209e0f0001020110a12000000000011020..\Test\main.cpp 211 16测试程序2:int main(int argc, char *argv[]) { QApplication a(argc, argv); TestDataMes msg; memset(msg,0,sizeof(msg)); msg.m_head.init(0x9e,sizeof(TestDataMes) - 1,0x0201,0x1001,0xA1,0x20); msg.m_i32 1234; msg.m_uc8 3; QByteArray ba; ba.append((const char *)msg,sizeof(msg)); qDebug() ba.toHex(); QByteArray changed StructToNet(msg); qDebug() changed.toHex(); TestDataMes msg1; NetToStruct(changed,msg1); { QByteArray ba; ba.append((const char *)msg1,sizeof(msg1)); qDebug() ba.toHex(); qDebug() __FILE__ __LINE__ msg1.m_i32 msg.m_uc8; } return a.exec(); }输出:9e1a0001020110a12000000000d2040000000000000000000000039e001a02011001a12000000000000004d2000000000000000000039e1a0001020110a12000000000d204000000000000000000000003..\Test\main.cpp 210 1234 3生成QDataStream的和重载函数的脚本文件gen_struct_code.pyimport re # 在这里粘贴你的所有结构体文本 struct_src typedef struct tagMainCtrlBitReplyMes { HEAD m_head; // 报文头 unsigned char m_MainCtrlStatus; // 主控板状态00H:正常 11H:异常 unsigned char m_SerStatus; // 伺服板状态00H:正常 11H:异常 unsigned char m_CheckSum; // 校验和 }MainCtrlBitReplyMes; typedef struct tagTestDataMes { HEAD m_head; int m_i32; unsigned int m_ui32; short m_i16; unsigned short m_ui16; char m_c8; unsigned char m_uc8; }TestDataMes; # def parse_structs(text): pat re.compile(rtypedef struct\s(\w)\s*\{(.*?)\}\s*(\w);, re.S) res [] for m in pat.finditer(text): struct_tag m.group(1) body m.group(2).strip() struct_name m.group(3) fields [] lines [line.strip() for line in body.splitlines() if line.strip()] for line in lines: line_no_comment re.sub(r//.*, , line).strip() if not line_no_comment: continue pure line_no_comment.rstrip(;) parts pure.split() var parts[-1] ty .join(parts[:-1]) fields.append((ty, var)) res.append((struct_name, fields)) return res def gen_cpp_code(struct_list): output [] for name, fields in struct_list: output.append(f// {name} 序列化反序列化 ) # // 输出 operator output.append(fQDataStream operator(QDataStream ds, const {name} msg)) output.append({) for _, var in fields: output.append(f ds msg.{var};) output.append( return ds;) output.append(}\n) # // 输出 operator output.append(fQDataStream operator(QDataStream ds, {name} msg)) output.append({) for _, var in fields: output.append(f ds msg.{var};) output.append( return ds;) output.append(}\n\n) return \n.join(output) if __name__ __main__: structs parse_structs(struct_src) code gen_cpp_code(structs) print( 生成完成复制下面全部代码到Qt头文件 ) print(code) # 自动输出到文本文件 with open(serial_code.h, w, encodingutf-8) as f: f.write(code) print(\n文件已自动生成serial_code.h和脚本同一文件夹)执行:python gen_struct_code.py打印: 生成完成复制下面全部代码到Qt头文件 // MainCtrlBitReplyMes 序列化反序列化 QDataStream operator(QDataStream ds, const MainCtrlBitReplyMes msg) { ds msg.m_head; ds msg.m_MainCtrlStatus; ds msg.m_SerStatus; ds msg.m_CheckSum; return ds; } QDataStream operator(QDataStream ds, MainCtrlBitReplyMes msg) { ds msg.m_head; ds msg.m_MainCtrlStatus; ds msg.m_SerStatus; ds msg.m_CheckSum; return ds; } // TestDataMes 序列化反序列化 QDataStream operator(QDataStream ds, const TestDataMes msg) { ds msg.m_head; ds msg.m_i32; ds msg.m_ui32; ds msg.m_i16; ds msg.m_ui16; ds msg.m_c8; ds msg.m_uc8; return ds; } QDataStream operator(QDataStream ds, TestDataMes msg) { ds msg.m_head; ds msg.m_i32; ds msg.m_ui32; ds msg.m_i16; ds msg.m_ui16; ds msg.m_c8; ds msg.m_uc8; return ds; } 文件已自动生成serial_code.h和脚本同一文件夹

相关新闻

最新新闻

11.PostgreSQ、-MySQL与MongoDB-AI应用如何选择数据库

11.PostgreSQ、-MySQL与MongoDB-AI应用如何选择数据库

PostgreSQL、MySQL 与 MongoDB:AI 应用该如何选择数据库? 码海寻道 大模型、智能体与 RAG 工程组件系列第 11 篇 做大模型应用时,数据库选型经常被简化成一句话:PostgreSQL 功能最全,MySQL 最常见,MongoDB…

2026/8/25 14:24:43
UE5使用蓝图实现简单角色跳跃案例

UE5使用蓝图实现简单角色跳跃案例

前言:基于该文章实现的案例上制作的动画案例《UE5简单角色移动案例》 1 创建IA_JUMP输入操作 右键资产浏览器,新建->输入->输入操作,命名为IA_JUMP 点进去,找到值类型设置为Digital(Bool)数字,保存后就可以关闭…

2026/8/25 14:24:43
《数学少年-从正负号到几何原本》(第九章:“万物建模,方程作答“)--9.3 岁月与选择

《数学少年-从正负号到几何原本》(第九章:“万物建模,方程作答“)--9.3 岁月与选择

第九章 万物建模,方程作答 卷首语:生活里所有等量难题,都能翻译成含未知数的等式。把现实故事转化为数学模型,答案自然清晰浮现。 9.3 岁月与选择 连续学完配比工程、行程买卖两大板块,苏晚和林默已经熟练掌握了看得…

2026/8/25 14:24:43
php json schema 你的PHP JSON Schema验证,真能像健身库一样,一个git clone就搞定吗?

php json schema 你的PHP JSON Schema验证,真能像健身库一样,一个git clone就搞定吗?

1324个动作, 1324张缩略情形图态, 1324个动态影像图, 5种语言——全都免费开放代码。你距"AI私教"仅缺一次从远程仓库复制到本地的操作。如果你有过做健身App的想法,你一定经历过这个噩梦:设想添加一个“哑铃飞鸟”动作, 接着翻找图片, 然后寻…

2026/8/25 14:24:43
体育馆赛事管理系统源码 Java+SpringBoot+Vue 前后分离

体育馆赛事管理系统源码 Java+SpringBoot+Vue 前后分离

一、关键词体育馆赛事管理系统,体育赛事场馆一体化管理系统,体育馆赛事运维管理平台二、作品包含源码数据库全套环境和工具资源本地部署教程三、项目技术前端技术:Html、Css、Js、Vue2、Element-ui后端技术:Java、SpringBoot2、My…

2026/8/25 14:24:43
【案例说明】融合知识图谱、大语言模型和 AI Agent 完成能源领域知识工程工作

【案例说明】融合知识图谱、大语言模型和 AI Agent 完成能源领域知识工程工作

关于知识工程,前面写过【新手上路常见问答】关于知识工程-CSDN博客从知识到智慧:知识图谱还要走多远?_cayley 知识图谱-CSDN博客【学习资源】知识图谱与大语言模型融合_oneke知识图谱-CSDN博客随着技术的快速发展,知识工程的实现有…

2026/8/25 14:19:43