字节序转换 + 模板 这里有一个结构体:// 类型别名对齐协议 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和脚本同一文件夹

相关新闻

最新新闻

DeepChem分子指纹终极指南:ECFP与FCFP如何选择?新手必看!

DeepChem分子指纹终极指南:ECFP与FCFP如何选择?新手必看!

DeepChem分子指纹终极指南:ECFP与FCFP如何选择?新手必看! 【免费下载链接】deepchem Democratizing Deep-Learning for Drug Discovery, Quantum Chemistry, Materials Science and Biology 项目地址: https://gitcode.com/GitHub_Trending…

2026/7/3 19:43:45
3个ExplorerPatcher部署故障的深度诊断与实战解决方案

3个ExplorerPatcher部署故障的深度诊断与实战解决方案

3个ExplorerPatcher部署故障的深度诊断与实战解决方案 【免费下载链接】ExplorerPatcher This project aims to enhance the working environment on Windows 项目地址: https://gitcode.com/GitHub_Trending/ex/ExplorerPatcher ExplorerPatcher作为Windows系统界面自定…

2026/7/3 19:43:45
SPI EEPROM与dsPIC30F硬件设计及数据存储管理

SPI EEPROM与dsPIC30F硬件设计及数据存储管理

1. 项目背景与硬件选型解析在嵌入式系统设计中,非易失性存储方案的选择直接影响产品的可靠性和用户体验。M95M04这颗4Mbit SPI EEPROM与dsPIC30F4011微控制器的组合,为存储用户偏好、日程设置等关键数据提供了理想的硬件平台。M95M04作为STMicroelectron…

2026/7/3 19:43:45
Gemini与Claude视觉创作能力实战对比:生成式AI工具选型指南

Gemini与Claude视觉创作能力实战对比:生成式AI工具选型指南

1. 项目概述:一场关于“视觉创作权”的真实较量2026年春天,我坐在工作室里,面前摊着三台设备:一台在跑Gemini 3.1 Pro生成的5秒海滩视频,一台正用Claude 3.5 Sonnet输出一份带交互节点的“量子计算原理”SVG流程图&…

2026/7/3 19:43:45
微信JS-SDK实现PC网页跳转小程序的Nuxt3实践

微信JS-SDK实现PC网页跳转小程序的Nuxt3实践

1. 项目背景与需求分析最近在开发一个企业官网项目时,客户提出了一个看似简单但实现起来颇有挑战的需求:需要在PC端网页中直接跳转到他们的小程序。这个需求在电商、教育、服务预约类网站中非常常见——当用户在电脑上浏览商品或服务时,能一键…

2026/7/3 19:43:45
NanoClaw:轻量级本地智能体框架,纯离线运行的文档处理助手

NanoClaw:轻量级本地智能体框架,纯离线运行的文档处理助手

1. 项目概述:为什么“本地优先”的轻量级智能体正在成为新刚需最近三个月,我陆续给六家中小团队做过技术咨询,几乎每场都会被问到同一个问题:“有没有一种智能体,不依赖云端API、不上传数据、不绑定厂商、装上就能跑&a…

2026/7/3 19:38:44

周新闻

月新闻