读取和写入结构的向量到文件

时间:2010-11-29 01:15:22

标签: c++ vector fstream

我已经阅读了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;        
};

2 个答案:

答案 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;
}