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

SQLite3 Blob读取时boost::binary_iarchive替代text_iarchive异常排查

问题背景

我对流与缓冲区相关技术不算精通,但着手处理这个问题的一个月里已经积累了大量相关实践经验。
本次问题涉及boost::serialization模块:若能成功启用二进制归档能力,可节省50%的存储空间。我检索了大量相关资料,拼凑出了当前可正常运行的代码,但这套代码仅支持text_iarchive。当我尝试替换为binary_iarchive时,程序会触发段错误,报错信息为boost serialize allocate(size_t n) 'n' exceeds maximum supported size,或是出现其他可明确判定为输入流/缓冲区与binary_iarchive预期数据格式不匹配的错误。

当前代码使用text_iarchive时运行完全正常:可以通过text_oarchive序列化数据写入SQLite3 Blob字段,在数据库中验证数据正确性,读取数据后通过text_iarchive反序列化还原为复杂对象,全流程无异常。
本次需要确认输入流与缓冲区的配置方式是否存在错误。

为避免无关信息干扰,此处不贴出序列化/反序列化目标对象的具体结构:该对象包含多个vector<double>、一个Eigen Matrix以及若干基础对象,这部分逻辑运行完全正常,不属于问题诱因(测试时每次都会删除数据库旧记录,不会出现将text_oarchive生成的归档读入binary_iarchive的格式不匹配情况)。

相关代码

输出归档段代码

该段代码使用text_oarchive或binary_oarchive时均运行正常,写入数据库的Blob字段结构符合预期:

// BinaryData is a Typedef for std::vector<char>
BinaryData serializedDataStream;
bio::stream<bio::back_insert_device<BinaryData>> outbuf {serializedDataStream};

// 切换到二进制归档时,原本的操作是将text_oarchive替换为binary_oarchive,并取消std::ios::binary参数的注释
boost::archive::text_oarchive outStream(outbuf); //, std::ios::binary);
outStream << ssInputDataAndBestModel_->theModel_;
outbuf.flush();
// SQLite3的Blob类型要求传入unsigned char类型数据,此处做类型转换
std::vector<unsigned char> buffer(serializedDataStream.begin(),serializedDataStream.end());

// 后续将buffer传入SQLite处理对象,存入Blob字段

输入归档段代码

无论存储的是文本格式还是二进制格式归档,从数据库读取取出的Blob数据与写入时完全一致(文本格式与二进制格式的Blob内容本身存在差异):

// 从SQLite3中读取Blob字段
currentModelDBRecPtr = cpp17::any_cast<dbo::ptr<Model>>(modelListModel);
if (!currentModelDBRecPtr->theModel.empty()) {
    
    // boost::serialize要求传入char类型数据,Blob存储的是unsigned char数组,此处做类型转换
    std::vector<char> blobBuffer(currentModelDBRecPtr->theModel.begin(), currentModelDBRecPtr->theModel.end());
    boost::iostreams::stream<boost::iostreams::array_source> membuf(blobBuffer.data(), blobBuffer.size());

    std::istream &input_stream = membuf;

    // 切换到二进制归档时,原本的操作是将text_iarchive替换为binary_iarchive,并取消std::ios::binary参数的注释
    boost::archive::text_iarchive input_archive(input_stream); //, std::ios::binary);
    TheModel inputArchiveModel;

    // 执行到下一行时崩溃,崩溃前已经能成功还原一半对象,崩溃位置随机
    input_archive >> inputArchiveModel;
}
问题现象

反序列化执行到input_archive >> inputArchiveModel;行时程序崩溃,但崩溃前已经成功还原了一半的对象结构,崩溃位置随机。

问题根因

你踩了Boost.Serialization和Boost.Iostreams配合时最常见的坑:

  • 传给binary_oarchive/binary_iarchive构造函数的std::ios::binary参数完全不会修改底层流的打开模式,这个参数只是archive自身的配置标记,不影响流的行为,之前修改这个参数的操作没有实际作用。
  • boost::iostreams::stream默认以文本模式打开设备,在Windows平台下文本模式会自动做换行符转换:写入时把单字节0x0A替换成双字节0x0D 0x0A,读取时反向替换。二进制归档里的0x0A只是普通数值字节,被篡改后整个归档的字节偏移、长度字段会完全错乱,就会出现「allocate size超过最大值」的报错、随机位置崩溃的现象——这和描述的「能还原一半对象再崩溃」完全吻合:前半段数据刚好没出现0x0A字节所以解析正常,读到被篡改的位置就直接解析失败。
  • 现有代码中vector<char>和vector<unsigned char>互转逻辑、SQLite Blob读写逻辑都是正确的,不是问题来源。
修复方案

只需要修改流初始化的代码,显式给Boost.Iostreams的流指定二进制打开模式即可:

  1. 修改输出段流初始化代码,给输出流加二进制打开标记,移除传给archive的无效std::ios::binary参数:
    // 初始化输出流时直接指定二进制打开模式
    bio::stream<bio::back_insert_device<BinaryData>> outbuf {serializedDataStream, std::ios::out | std::ios::binary};
    // 二进制归档初始化不需要额外传std::ios::binary参数
    boost::archive::binary_oarchive outStream(outbuf);
    
  2. 修改输入段流初始化代码,给输入流加二进制打开标记,移除传给archive的无效std::ios::binary参数:
    // 初始化输入流时直接指定二进制打开模式
    boost::iostreams::stream<boost::iostreams::array_source> membuf(blobBuffer.data(), blobBuffer.size(), std::ios::in | std::ios::binary);
    std::istream &input_stream = membuf;
    // 二进制归档初始化不需要额外传std::ios::binary参数
    boost::archive::binary_iarchive input_archive(input_stream);
    
  3. 注意保持变量的初始化/析构顺序:现有代码是先定义流、再定义archive,离开作用域时archive先析构(会完成剩余归档数据的读写)、流再析构,这个顺序是正确的,不需要调整。手动调用outbuf.flush()的逻辑可以保留,不会产生副作用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 05:00:46