写入/读取 std::映射到二进制文件需要运算符

Writing/Reading a std::map to a binary file require operator

本文关键字:二进制文件 运算符 映射 读取 std 写入      更新时间:2023-10-16

我想写一个std::map到一个文件并读回去。我正在寻找一种相当简单和简约的方式来做到这一点,没有助推器。我发现像这里这样的矢量是可行的 使用迭代器正确读取 std::vector 到文件中

发现这个问题与我想做的事情有关,除了我正在寻找二进制替代方案。

将键值对文件读入 std::map

对于不涉及动态内存(实际上是指针(的类型

template<size_t N>
struct Serial
{
    char bin[N];
    friend ostream& operator<<(ostream& os, const Serial& s)
    {
        for(auto c : bin)
            os << c;
        return os;
    }
    friend istream& operator>>(istream& is, Serial& s)
    {
        for(auto& c : bin)
            is >> c;
        return is;
    }
};
struct Key
{
    static constexpr size_t size = sizeof(Key);
    Key(const Serial<size>& s) { memcpy(this, s.bin, size); }
    Serial<size>& serialize() { return *reinterpret_cast<Serial<size>*>(this); }
};
struct Value
{
    static constexpr size_t size = sizeof(Value);
    Key(const Serial<size>& s) { memcpy(this, s.bin, size); }
    Serial<size>& serialize() { return *reinterpret_cast<Serial<size>*>(this); }
};
void write(ostream& os, const std::map<Key, Value>& m)
{
    for(const auto& p : m)
        os << p.first.serialize() << p.second.serialize();
}
void read(istream& is, std::map<Key, Value>& m)
{
    Serial<Key::size> k;
    Serial<Value::size> v;
    while(is >> k >> v)
        m[k] = v;
}

对于涉及动态内存(指针(的类型,解决方案将完全取决于它们的工作方式,无法提供神奇的解决方案。

你考虑过 JSON 吗?

欢迎来到混乱、混乱、不一致的序列化世界。希望你喜欢这次骑行!!

这是一个古老的问题:如何将一个适度复杂的数据结构写入某种文本或二进制格式,然后能够稍后将其读回。有几种不同的方法可以做到这一点。但是,您说要序列化为二进制格式,因此我建议使用 MessagePack。

有一个C++11库用于处理名为msgpack11的MessagePack格式,它也相当轻量级,似乎符合您的要求。下面是一个示例:

std::map<A, B> my_map;
// To save my_map:
msgpack11::MsgPack msgpack{my_map};
std::string binary_data = msgpack.dump();
// Now you can save binary_data to a file.
// To get the map back:
string error_string;
auto msgpack = msgpack11::MsgPack::parse(binary_data, error_string);
std::map<A, B> my_map;
// Now you need to manually read back the data.

对于二进制写入,您应该使用 ostream 的写入方法

ostream& ostream::write (const char* s, streamsize n);

请参阅此处的文档:http://en.cppreference.com/w/cpp/io/basic_ostream

你不能把地图写成文件,你应该写它的reresntation,由你开发。您需要单独写入每个键/值对,或者将它们缓冲为数据块并将其写入文件。不过,这真的并不比 for 循环复杂多少。如果 map 包含的类不是简单构造和销毁的,则应实现一个允许序列化类的二进制数据的方法。

二进制

实现必然是不可移植的(对于生成的文件(。如果这不是问题,请考虑定义使用内存映射文件的自定义分配器。然后,您将使用该分配器作为模板参数之一来声明您的 std:map。您可以直接使用该地图,也可以使用范围插入将现有地图保存到文件中。如果键或值需要分配器(例如字符串(,则必须使用模板声明中的内存映射分配器声明这些类型的版本,并定义从键/值类型到新类型的赋值运算符。您可以通过搜索"内存映射文件 stl 分配器"找到一些分配器实现和进一步的讨论。另请参阅: stl vector 中的内存映射文件存储

void BinSerialize(ostream &out, int32_t x);
void BinSerialize(ostream &out, int16_t x);
void BinSerialize(ostream &out, int8_t x);
void BinSerialize(ostream &out, const string &s)
{
    BinSerialize(out, (int32_t)s.size());
    out.write(size.c_str(), s.size()));
}
temmplate<class KeyT, ValueT>
void BinSerialize(ostream &out, const std::map<KeyT, ValueT> &m)
{
    BinSerialize(out, (int32_t)m.size());
    for (auto& item : m)
    {
        BinSerialize(out, item.first);
        BinSerialize(out, item.second);
    }
}
void BinDeserialize(istream &input, int32& x);
void BinDeserialize(istream &input, int16& x);
void BinDeserialize(istream &input, int8& x);
void BinDeserialize(istream &input, string &s)
{
    int32_t size;
    BinDerialize(out, size);
    s.resize(size);
    out.read(size.c_str(), size);
}
temmplate<class KeyT, class ValueT>
void BinDeserialize(istream &input, std::map<KeyT, ValueT> &m)
{
    int32_t size;
    m.clear();
    BinDeserialize(out, size);
    for (int32_t i=0; i<size; ++i)
    {
        std::pair<KeyT, ValueT> item;
        BinDeserialize(out, item.first);
        BinDeserialize(out, item.second);
        m.insert(item);
    }
}

这写得很快。可以使用模板对其进行改进,以涵盖所有基本类型和所有 STL 容器。

另外,记住字节序会很好。

在这种情况下,最好避免使用重载运算符。但是,如果要这样做,最好定义类,该类将包装STL流并将拥有自己的重载>> <<运算符集。看看Qt QDataStream .