You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Visual Studio下C++结构体多数据类型二进制一次性序列化与反序列化方案咨询

可行的二进制序列化/反序列化方案(无第三方依赖)

直接用fwrite(&frame, sizeof(Frame), 1, file)这种方式读写整个结构体是行不通的——问题就出在std::string上:它内部存储的是指向堆内存的指针,而不是字符串的实际内容。写入文件的只是这个指针的地址,反序列化时读回来的地址在当前进程中是无效的,自然会触发内存错误。

不过我们可以自己实现一套轻量的序列化逻辑,把结构体的所有数据(包括字符串的实际内容)按固定格式写入文件,反序列化时再按格式重建结构体。虽然不是字面意义上的“一次性读写整个结构体”,但可以封装成两个函数,调用起来和一次性操作一样方便,同时完全不依赖第三方库。

具体实现步骤

核心思路是:先序列化所有POD类型(bool、uint64_t、uint32_t、float这些都是POD),再单独处理std::string——先写字符串的长度,再写字符串的字节内容。反序列化时按完全相反的顺序读取。

下面是针对你的Frame结构体的完整代码示例:

#include <fstream>
#include <string>
#include <cstdint>

struct Frame { 
    bool isPass{ true }; 
    uint64_t address{ 0 }; 
    uint32_t age{ 0 }; 
    float marks{ 0.0 }; 
    std::string userName; 
};

// 序列化Frame到二进制文件
bool serializeFrame(const Frame& frame, const std::string& filePath) {
    std::ofstream file(filePath, std::ios::binary);
    if (!file.is_open()) return false;

    // 写入POD类型数据
    file.write(reinterpret_cast<const char*>(&frame.isPass), sizeof(frame.isPass));
    file.write(reinterpret_cast<const char*>(&frame.address), sizeof(frame.address));
    file.write(reinterpret_cast<const char*>(&frame.age), sizeof(frame.age));
    file.write(reinterpret_cast<const char*>(&frame.marks), sizeof(frame.marks));

    // 处理std::string:先写长度(用uint32_t保证跨平台兼容性)
    uint32_t strLen = static_cast<uint32_t>(frame.userName.size());
    file.write(reinterpret_cast<const char*>(&strLen), sizeof(strLen));
    // 再写字符串内容
    if (strLen > 0) {
        file.write(frame.userName.data(), strLen);
    }

    return file.good();
}

// 从二进制文件反序列化Frame
bool deserializeFrame(Frame& frame, const std::string& filePath) {
    std::ifstream file(filePath, std::ios::binary);
    if (!file.is_open()) return false;

    // 读取POD类型数据
    file.read(reinterpret_cast<char*>(&frame.isPass), sizeof(frame.isPass));
    file.read(reinterpret_cast<char*>(&frame.address), sizeof(frame.address));
    file.read(reinterpret_cast<char*>(&frame.age), sizeof(frame.age));
    file.read(reinterpret_cast<char*>(&frame.marks), sizeof(frame.marks));

    // 处理std::string:先读长度
    uint32_t strLen = 0;
    file.read(reinterpret_cast<char*>(&strLen), sizeof(strLen));
    // 分配空间并读取内容
    frame.userName.resize(strLen);
    if (strLen > 0) {
        file.read(&frame.userName[0], strLen);
    }

    return file.good();
}

// 测试示例
int main() {
    Frame originalFrame;
    originalFrame.isPass = false;
    originalFrame.address = 0x123456789ABCDEF0;
    originalFrame.age = 25;
    originalFrame.marks = 92.5f;
    originalFrame.userName = "JohnDoe";

    // 序列化
    if (serializeFrame(originalFrame, "frame.bin")) {
        Frame restoredFrame;
        // 反序列化
        if (deserializeFrame(restoredFrame, "frame.bin")) {
            // 验证数据是否一致
            bool isValid = (originalFrame.isPass == restoredFrame.isPass) &&
                           (originalFrame.address == restoredFrame.address) &&
                           (originalFrame.age == restoredFrame.age) &&
                           (originalFrame.marks == restoredFrame.marks) &&
                           (originalFrame.userName == restoredFrame.userName);
            // isValid应该为true
        }
    }

    return 0;
}

关键细节说明

  • POD类型直接读写:像bool、uint64_t这些POD类型可以直接用write/read操作,因为它们的内存布局是连续且固定的。
  • std::string的处理:必须先写入字符串的长度,这样反序列化时才能知道要分配多少内存来存储字符串内容,避免内存溢出或截断。
  • 字节序(可选):如果需要跨不同架构的平台(比如x86和ARM)读写文件,需要把整数类型(比如uint64_t、uint32_t)转换成网络字节序再写入,读取时再转换回来。可以用htonll/ntohll(处理64位)、htonl/ntohl(处理32位)这类函数。如果只在同一平台使用,这一步可以省略。
  • 封装性:把序列化和反序列化逻辑封装成函数后,调用方只需要传入结构体和文件路径,和“一次性读写”的体验几乎一致,同时避免了逐个字段处理的繁琐。

这种方案完全不需要依赖任何开源库,只用到C++标准库的功能,同时完美解决std::string的内存分配问题。

内容的提问来源于stack exchange,提问作者Rav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 05:34:05