我已经阅读了Stack Overflow和其他一些关于向文件写入向量的网站上的一些帖子。我已经实现了我认为有效的方法,但我遇到了一些麻烦。结构中的一个数据成员是类字符串,当重新读入向量时,该数据将丢失。此外,在编写第一次迭代之后,额外的迭代会导致malloc错误。如何修改下面的代码以实现将矢量保存到文件所需的能力,然后在程序再次启动时将其读回?目前,读取是在构造函数中完成的,在析构函数中写入,类的唯一数据成员是向量,但是具有操作该向量的方法。
这是我的读/写方法的要点。假设vector<element> elements
...
读:
ifstream infile;
infile.open("data.dat", ios::in | ios::binary);
infile.seekg (0, ios::end);
elements.resize(infile.tellg()/sizeof(element));
infile.seekg (0, ios::beg);
infile.read( (char *) &elements[0], elements.capacity()*sizeof(element));
infile.close();
写:
ofstream outfile;
outfile.open("data.dat", ios::out | ios::binary | ios_base::trunc);
elements.resize(elements.size());
outfile.write( (char *) &elements[0], elements.size() * sizeof(element));
outfile.close();
结构元素:
struct element {
int id;
string test;
int other;
};
答案 0 :(得分:6)
在C ++中,内存通常不能直接像这样直接读写磁盘。特别是,您的struct element
包含string
,这是非POD数据类型,因此无法直接访问。
思考实验可能有助于澄清这一点。您的代码假定您的所有element
值都相同。如果其中一个string test
值超过您的假设,会发生什么?您的代码如何知道在读取和写入磁盘时要使用的大小?
有关如何处理此问题的详细信息,请阅读serialization。
答案 1 :(得分:2)
您的代码假设所有相关数据都直接存在于向量中,而字符串是固定大小的对象,这些对象具有可以在堆上添加其可变大小内容的指针。你基本上是保存指针而不是文本。您应该编写一些字符串序列化代码,例如:
bool write_string(std::ostream& os, const std::string& s)
{
size_t n = s.size();
return os.write(n, sizeof n) && os.write(s.data(), n);
}
然后你可以为你的struct编写序列化例程。有几个设计选项: - 许多人喜欢声明可以容纳std :: ostream的Binary_IStream / Binary_OStream类型,但是作为一个不同的类型可以用来创建一组单独的序列化例程ala:
operator<<(Binary_OStream& os, const Some_Class&);
或者,在处理二进制序列化时,您可以放弃通常的流表示法,而是使用函数调用表示法。显然,让相同的代码正确输出二进制序列化和人类可读的序列化是很好的,因此基于操作符的方法很有吸引力。
如果序列化数字,则需要决定是以二进制格式还是ASCII格式。使用纯二进制格式,需要可移植(甚至在同一操作系统上的32位和64位编译之间),您可能需要做一些努力来编码和使用类型大小元数据(例如int32_t或int64_t?)作为字节序(例如考虑网络字节顺序和ntohl() - 族函数)。使用ASCII,您可以避免某些注意事项,但它的长度可变,写入/读取速度较慢。下面,我随意使用带有'|'的ASCII数字的终结者。
bool write_element(std::ostream& os, const element& e)
{
return (os << e.id << '|') && write_string(os, e.test) && (os << e.other << '|');
}
然后为你的载体:
os << elements.size() << '|';
for (std::vector<element>::const_iterator i = elements.begin();
i != elements.end(); ++i)
write_element(os, *i);
要读回来:
std::vector<element> elements;
size_t n;
if (is >> n)
for (int i = 0; i < n; ++i)
{
element e;
if (!read_element(is, e))
return false; // fail
elements.push_back(e);
}
......需要......
bool read_element(std::istream& is, element& e)
{
char c;
return (is >> e.id >> c) && c == '|' &&
read_string(is, e.test) &&
(is >> e.other >> c) && c == '|';
}
...和...
bool read_string(std::istream& is, std::string& s)
{
size_t n;
char c;
if ((is >> n >> c) && c == '|')
{
s.resize(n);
return is.read(s.data(), n);
}
return false;
}