如何在 C++ 中使用 Boost 仅反序列化文件的一部分?

How to deserialize only parts of a file using Boost in C++?

我有许多使用 boost::archive::binary_oarchive 序列化的相同 class 的实例。它们按给定顺序保存。我只对加载其中一个感兴趣并且我知道它的位置。如何检索(反序列化)一个对象而不必反序列化几乎所有对象?

更一般地说,从文件中仅检索某些对象的最佳方法是什么?

现在,我的代码看起来像这样:

std::ofstream saveFile("savefile.save");

boost::archive::binary_oarchive oa(saveFile);
oa << arrayOfObjects;
        
saveFile.close();

// Later...

std::ifstream loadFile("savefile.save");

boost::archive::binary_iarchive ia(loadFile);
ia >> arrayOfObjects;

auto oneSpecificObject = arrayOfObjects[i]; // I have to do this; not efficient

loadFile.close();

提前致谢并干杯,

这完全取决于 arrayOfObjects 的确切类型。

因为是事情如何序列化的决定性因素。

如果是真正的数组,事情可能不会太复杂。尽管一旦涉及到对象跟踪,它就会再次变得非常棘手。例如

Live On Coliru

X x{"the answer is 42"};
// std::vector arrayOfObject { &x, &x, &x, &x, &x, &x, &x, &x }; // OR:
X* arrayOfObject[] = { &x, &x, &x, &x, &x, &x, &x, &x };

{
    boost::archive::text_oarchive oa(std::cout);
    oa << arrayOfObject;
}

版画

22 serialization::archive 19 8 0 1 0
0 16 the answer is 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0

因此,天真地只读回第 6 个元素会导致……未指定的结果。因此,我的建议是/只读取整个数组/并丢弃所有不需要的数据。

破解它

如果我们不安全,假设没有上述的复杂因素并取决于实现细节(例如向量实际如何序列化),您可以编写反序列化来匹配并获得您希望的行为:

Live On Coliru - Don't Try This At Home

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/preprocessor.hpp>
#include <iostream>
#include <sstream>

#ifndef TYPE
#define TYPE xml
#endif
using oarchive = boost::archive::BOOST_PP_CAT(TYPE, _oarchive);
using iarchive = boost::archive::BOOST_PP_CAT(TYPE, _iarchive);

struct X {
    std::string answer;
    void serialize(auto& ar, unsigned) { ar& BOOST_SERIALIZATION_NVP(answer); }
};

template <typename T, size_t TargetIndex>
struct FakeVectorReader {
    T element;

    template <typename Ar> void serialize(Ar& ar, unsigned)
    {
        static_assert(typename Ar::is_loading{});
        using namespace boost::serialization;

        collection_size_type count;
        ar >> make_nvp("count", count);

        if (library_version_type(3) < ar.get_library_version()) {
            item_version_type item_version(0);
            ar >> make_nvp("item_version", item_version);
        }

        assert(count > TargetIndex);

        T v;
        for (size_t i = 0; i < count; ++i) {
            ar >> make_nvp("item", v);
            if (i == TargetIndex) {
                element = std::move(v);
                ar.reset_object_address(&element, &v); // a bit half-hearted, this
            }
        }
    }
};

int main()
{
    std::vector const arrayOfObject{
        X{"zero"}, {"one"}, {"two"},   {"three"}, {"four"},
        {"five"},  {"six"}, {"seven"}, {"eight"}, {"nine"},
    };

    std::stringstream ss;
    {
        oarchive oa(ss);
        oa << BOOST_SERIALIZATION_NVP(arrayOfObject);
    }

    if (std::string("binary") != BOOST_PP_STRINGIZE(TYPE)) {
        std::cout << ss.str() << std::endl;
    }

    {
        iarchive ia(ss);
        FakeVectorReader<X, 6> hack;
        ia >> boost::serialization::make_nvp("arrayOfObject", hack);

        std::cout << "hack.element: " << hack.element.answer << "\n";
    }
}

打印

22 serialization::archive 19 0 0 10 0 0 0 4 zero 3 one 3 two 5 three 4 four 4 five 3 six 5 seven 5 eight 4 nine
hack.element: six

不要在家尝试这个

我相信你会明智地使用这些知识。

  • 我深入研究了实现细节,
  • 做了一个 half-hearted lip-service 对象跟踪,知道当对象在存档中实际别名时它会中断
  • 请注意,如果您更改容器类型或
  • ,所有这些都会中断
  • 甚至升级到更新版本的 Boost 库。
  • 请记住,所有元素实际上都是反序列化的(这样一来,如果数组后面有更多数据,那么至少您仍然可以读取它)。
  • 如果X不安全就会泄漏(遵循Rule-Of-3/5/0)

我希望答案有助于说明为什么你不应该这样做,也许如果你以某种方式不能避免这种情况(你需要反序列化其中的一部分multi-terabyte存档,但不能再访问超级计算机了?)