应直接使用 msgpack-c 官方库,它头文件即用、无依赖、支持 c++11+;序列化需手动 pack 到 sbuffer 再 binary 写入文件,读取前必须校验文件完整性,否则易报 parse_error。

MessagePack C++ 库怎么选:msgpack-c 是唯一实用选择
直接用 msgpack-c(官方 C++ 实现),别碰其他封装或旧版分支。它头文件即用、无依赖、支持 C++11+,且文档虽简但接口稳定。网上有些教程推荐的 msgpack11 或自行封装的 JSON 风格 wrapper,实际序列化效率低、不支持二进制 blob、无法控制 packer/unpacker 行为,容易在读写大 buffer 时崩溃。
安装方式(推荐):
- Linux/macOS:
git clone https://github.com/msgpack/msgpack-c.git && cd msgpack-c && mkdir build && cd build && cmake .. && make install - Windows(MSVC):用 vcpkg:
vcpkg install msgpack-c:x64-windows - 头文件直连(最轻量):下载
include/msgpack.hpp单头文件,#include "msgpack.hpp"即可,但需确保编译器支持 C++11 的constexpr和auto
如何把 struct 写入文件:pack + ofstream.write 是标准路径
MessagePack 不提供直接“序列化到文件”的函数,必须手动 pack 成 msgpack::sbuffer 或 std::vector<char></char>,再用 std::ofstream 以 binary 模式写入。漏掉 std::ios::binary 会导致 Windows 下换行符被篡改,解包失败报 parse_error。
示例(写入一个简单结构):
#include <msgpack.hpp>
#include <fstream>
#include <vector>
struct Person {
std::string name;
int age;
MSGPACK_DEFINE(name, age);
};
int main() {
Person p{"Alice", 30};
msgpack::sbuffer sbuf;
msgpack::pack(sbuf, p);
std::ofstream ofs("person.mpk", std::ios::binary);
ofs.write(sbuf.data(), sbuf.size());
ofs.close();
return 0;
}
</vector></fstream></msgpack.hpp>
-
MSGPACK_DEFINE必须声明在 struct 内部,否则msgpack::pack编译不过 - 不要用
std::ofstream 写二进制数据——它会做格式化,破坏 MessagePack 格式 - 写完务必检查
ofs.good(),尤其在嵌入式或磁盘满场景下,write()成功不代表磁盘已落盘
读取时为什么总报 parse_error?unpack 前必须验证 buffer 完整性
msgpack::unpack 对输入极其敏感:少一个字节、多一个字节、开头不是 valid msgpack type marker,都会抛 msgpack::parse_error。常见原因不是代码写错,而是文件读取不完整或被截断。
- 读文件必须用
std::ifstream::read并校验返回值,不能只靠gcount()—— 它可能小于预期 - 建议先
seekg(0, std::ios::end)获取文件大小,再seekg(0),分配等长std::vector<char></char>,再read() - 读完后检查
ifs.gcount() == expected_size,不等就拒绝 unpack - 避免用
std::string存二进制数据(某些实现会在 \0 处截断),统一用std::vector<char></char>或msgpack::sbuffer
正确读取片段:
std::ifstream ifs("person.mpk", std::ios::binary);
ifs.seekg(0, std::ios::end);
size_t size = ifs.tellg();
ifs.seekg(0);
std::vector<char> buf(size);
ifs.read(buf.data(), size);
if (!ifs || ifs.gcount() != static_cast<:streamsize>(size)) {
throw std::runtime_error("failed to read full file");
}
msgpack::object_handle oh = msgpack::unpack(buf.data(), buf.size());
Person p = oh.get().as<person>();
</person></:streamsize></char>
性能关键点:避免反复构造 sbuffer,复用 packer/unpacker 实例
高频序列化场景(如日志、网络消息)中,每次调用 msgpack::pack 都新建 msgpack::sbuffer 和内部 packer,有明显内存分配开销。实际项目应复用对象:
- 用
msgpack::sbuffer成员变量,调用sbuf.clear()重置而非重建 - 显式构造
msgpack::packer<:sbuffer></:sbuffer>,绑定已有sbuf,后续调用packer.pack()更快 - 解包侧,
msgpack::unpack本身无状态,但若频繁解析同结构,可缓存msgpack::object_handle的get().as<t>()</t>调用路径(现代编译器通常已优化) - 注意:
msgpack::sbuffer不是线程安全的,多线程需各自持有实例
复用 packer 示例:
msgpack::sbuffer sbuf;
msgpack::packer<:sbuffer> pk(sbuf);
for (const auto& p : people_list) {
sbuf.clear(); // 关键:清空而非重建
pk.pack(p);
// ... write sbuf.data(), sbuf.size()
}
</:sbuffer>
文件序列化的真正难点不在语法,而在二进制 I/O 的边界处理和错误反馈缺失——parse_error 不告诉你哪一字节出错,只能靠预读校验和 fallback 日志。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











