std :: vector写/回读给垃圾

时间:2015-02-24 00:13:07

标签: c++ c++11 file-io stl stdvector

我正在尝试将类“Product”的向量写入文件并将其读回。但是我在阅读时会加载垃圾。有人可以回顾一下可能出错的地方吗?或者建议另一种方法。

#include <fstream>
#include <vector>
#include <iostream>

class Product
{
  public:
      std::string name;
      int code;
      double price;
};


int
main ()
{
  const char *const file_name = "products.bin";
  {
    std::vector < Product > prod
    {
      Product {"Honey", 1, 12.34}, 
      Product {"CoffeeBeans", 2, 56.78}, 
      Product {"Cl", 3, 90.12}
    };

    for (const Product & p:prod)
       std::cout << p.name << ' ' << p.code << ' ' << p.price << '\n';

    std::ofstream file (file_name, std::ios::binary);
    size_t sz = prod.size ();

    file.write (reinterpret_cast < const char *>(&sz), sizeof (sz));
    file.write (reinterpret_cast < const char *>(&prod[0]), sz * sizeof (prod[0]));
  }

  {
    std::vector < Product > prod;
    std::ifstream file (file_name, std::ios::binary);

    size_t sz;
    file.read (reinterpret_cast < char *>(&sz), sizeof (sz));
    prod.resize (sz);
    file.read (reinterpret_cast < char *>(&prod[0]), sz * sizeof (prod[0]));

    for (const Product & p:prod)
         std::cout << p.name << ' ' << p.code << ' ' << p.price << '\n';
  }

}

> Blockquote

4 个答案:

答案 0 :(得分:2)

标准库成员std::ostream::write将原始数据写入输出流。作为该写入的条件,所述数据本质上必须是trivially copyable

您的Product不是这种类型。它包含std::string成员。 std::string(通常)使用由对象内部指针管理的动态缓冲区实现。因此,它不是一个简单的可复制类型,因此Product不是一个简单的可复制类型。

如果您以原始字节存储此数据,则需要协议,读者和作者都同意这一点。这个协议的复杂程度完全取决于您以及您的最终需求多么多样,而且由于您是双方的作者,这些决定完全取决于您。如果目标文件应该是多平台/字节序独立的,那么事情会很快变得复杂。如果目标只是同一平台,您可以在开发协议时跳过一些其他繁琐的细微差别。

就个人而言,我会选择一个简单的分隔文本行/记录条目。你认为通过以bin格式执行此操作而节省的空间可能是微不足道的,并且开发读者和作者都是微不足道的。作为奖励,高度可能会免费获得平台和endian独立性。

祝你好运。

答案 1 :(得分:2)

为了编写和读取非POD类型,你真的应该为每个这样的对象编写自己的序列化和反序列化函数,它按照定义的顺序写入所有对象的成员,并按顺序读取它们。此外,如果您希望数据可移植,则应记住字节顺序。

最好也为POD类型使用序列化函数。原因如下:

  • 不同的编译器/平台可能有一些不同大小的数据类型
  • 不同的编译器/平台可能会以不同的顺序排列成员
  • 以上两者都可能导致额外的填充

在对象或其成员或vtable中也有潜在的指针,你不需要的东西,也不应该序列化。

如果您使用原始数据,所有这些都会有问题。

答案 2 :(得分:1)

非POD类型(在C ++ 11之前)或非平凡可复制类型(C ++ 11)的值不能在二进制复制操作中存活。这包括使用memcpy()创建副本,或者(正如您所做的)以二进制模式写入文件并回读。此类操作(memcpy(),二进制读取和写入)仅具有POD类型的定义含义。

std::string不是这种类型,因为(除此之外)它具有用户定义的构造函数,赋值运算符和管理资源(内存)的析构函数。包含不可复制类型实例的任何stuct / class类型也不是可复制类型。

答案 3 :(得分:0)

你不能写二进制文件,std :: string有动态内存。

您应该将成员写为成员,例如:

file<<prod[0].name<<' '<<prod[0].code<<' '<<prod[0].price<<' '; //You must separate diferent fields with spaces

以同样的方式阅读:

file>>prod[0].name>>prod[0].code>>prod[0].price