【问题标题】:reading and writing a vector of structs to file读取和写入结构向量到文件
【发布时间】:2010-11-29 01:15:21
【问题描述】:

我在 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;        
};

【问题讨论】:

  • 您使用的哪些 C++ 学习资料告诉您以这种方式进行文件访问?
  • 这是家庭作业的一部分,要求我们使用 fstream 将矢量数据读/写到文件中。由于我们没有完全涵盖在课堂上将矢量写入文件的复杂操作,因此我阅读了 fstream 文档等并查看了在线示例。我决定用二进制编写将是完成此任务的最简单方法。调整大小等某些细节是我选择做的限制文件大小的事情。我们的班级没有正式的书籍或资料。我们有一些在线说明,但它们的详细程度远不及实现我需要的内容。
  • 我决定将我的字符串更改为字符数组,并在代码的其他地方进行一些小的修改以支持该更改。切换到字符数组而不是字符串后,上面的代码似乎效果很好。

标签: c++ vector fstream


【解决方案1】:

在 C++ 中,一般不能像这样直接读写内存。特别是,您的struct element 包含string,它是非POD 数据类型,因此无法直接访问。

思想实验可能有助于澄清这一点。您的代码假定所有 element 值的大小相同。如果string test 值之一比您假设的要长,会发生什么情况?您的代码如何知道读写磁盘时使用的大小?

您需要阅读 serialization 以了解有关如何处理此问题的更多信息。

【讨论】:

  • 我可能会补充一点,如果您想在不同架构(字节序)的机器上读取/写入这些文件,您将不得不以字节序中立的方式写入/读取二进制文件,以便它可移植跨度>
  • 这是有道理的。我认为一个更简单的解决方案是切换到一个字符串。类字符串只是我的首选,让我更灵活。
【解决方案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);
}

然后您可以为您的结构编写序列化例程。有几个设计选项: - 许多人喜欢声明可以容纳 std::ostream 的 Binary_IStream / Binary_OStream 类型,但作为不同的类型可用于创建一组单独的序列化例程ala:

operator<<(Binary_OStream& os, const Some_Class&);

或者,您可以在处理二进制序列化时放弃通常的流式表示法,而改用函数调用表示法。显然,让相同的代码正确输出二进制序列化和人类可读的序列化是很好的,因此基于运算符的方法很有吸引力。

如果您对数字进行序列化,您需要决定是采用二进制格式还是 ASCII 格式。对于需要可移植的纯二进制格式(即使在同一操作系统上进行 32 位和 64 位之间的编译),您可能还需要努力编码和使用类型大小的元数据(例如 int32_t 或 int64_t?)作为字节序(例如考虑网络字节顺序和 ntohl()-family 函数)。使用 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;
}

【讨论】:

    猜你喜欢
    • 2016-09-04
    • 2013-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多