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

时间:2017-05-14 08:00:23

标签: c++ file c++11 iterator

我想将std :: map写入文件并将其读回。我正在寻找一种相当简单和极简主义的方式来做到这一点,而不是提升。我发现这对于像Reading and writing a std::vector into a file correctly with iterators

这样的矢量是可行的

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

reading a file of key-value pairs in to a std::map

5 个答案:

答案 0 :(得分:1)

对于没有动态内存(实际上是指针)的类型

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吗?

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

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

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

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

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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容器。

同样记住关于endian的事情也很好。

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