我想将std :: map写入文件并将其读回。我正在寻找一种相当简单和极简主义的方式来做到这一点,而不是提升。我发现这对于像Reading and writing a std::vector into a file correctly with iterators
这样的矢量是可行的我发现这个问题与我想做的事情有关,除了我正在寻找二元替代品。
答案 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
。