【问题标题】:how to correctly write vector to binary file in c++?如何在 C++ 中正确地将向量写入二进制文件?
【发布时间】:2012-12-14 21:14:24
【问题描述】:

感谢 Mats Petersson 对如何将向量复制到数组的解释,这似乎可行。这是代码片段:

#include <iostream>
#include <string.h>
#include <vector>
#include <fstream>

using namespace std;

class Student
  {
    private:
    char m_name[30];
    int m_score;

    public:
    Student()
      {

      }
    Student(const Student& copy)
      {
           m_score = copy.m_score;   //wonder why i can use this statment as
           strncpy(m_name, copy.m_name, 30); //declare it private
      }
      Student(const char name[], const int &score)
      :m_score(score)
      {
           strncpy(m_name, name, 30);
      }
      void print() const
      {
           cout.setf(ios::left);
           cout.width(20);
           cout << m_name << " " << m_score << endl;
      }
      };


      int main()
      {
        vector<Student> student;
        student.push_back(Student("Alex",19));
        student.push_back(Student("Maria",20));
        student.push_back(Student("muhamed",20));
        student.push_back(Student("Jeniffer",20));
        student.push_back(Student("Alex",20));
        student.push_back(Student("Maria",21));
      {
      Student temp[student.size()];
      unsigned int counter;
      for(counter = 0; counter < student.size(); ++counter)
      {
        temp[counter] = student[counter];
      }

      ofstream fout("data.dat", ios::out | ios::binary);
      fout.write((char*) &temp, sizeof(temp));
      fout.close();
      }

      vector<Student> student2;
      ifstream fin("data.dat", ios::in | ios::binary);

      {
        fin.seekg(0, ifstream::end);
        int size = fin.tellg() / sizeof (Student);
        Student temp2[size];
        fin.seekg(0, ifstream::beg);
        fin.read((char*)&temp2, sizeof(temp2));
        int counter;
        for(counter = 0; counter <6; ++counter)
        {
        student2.push_back(temp2[counter]);
        }
        fin.close();
      }
      vector<Student>::iterator itr = student2.begin();
      while(itr != student2.end())
      {
        itr->print();
        ++itr;
      }
      return 0;
      }

但是我客人这种方法会浪费大量内存并且很麻烦。也许我会考虑用豹猫和其他建议写它先生。 谢谢大家的回答。

【问题讨论】:

  • 标准提示:用g++ -Wall -g编译,改进你的代码直到没有给出警告,学习使用gdbvalgrind来调试它。
  • 好吧,我还没有习惯使用终端编译技术。我想我必须以某种方式学习它。感谢您的回复。
  • 那么,也学会使用make,写出你的简单Makefile
  • 好的,非常感谢,我会考虑的。

标签: c++ fstream


【解决方案1】:

要将vector&lt;T&gt; of PODs 存储在文件中,您必须写入向量的内容,而不是向量本身。您可以使用第一个元素的地址&amp;vector[0] 访问原始数据(假设它至少包含一个元素)。要获得原始数据长度,请将向量中的元素数乘以一个元素的大小:

strm.write(reinterpret_cast<const char*>(&vec[0]), vec.size()*sizeof(T));

当你从文件中读取向量时也是如此;元素计数是文件总大小除以一个元素的大小 (假设您只在文件中存储一种类型的 POD):

const size_t count = filesize / sizeof(T);
std::vector<T> vec(count);
strm.read(reinterpret_cast<char*>(&vec[0]), count*sizeof(T));

这仅适用于您可以根据文件大小计算元素数量(如果您只存储一种类型的 POD 或所有向量包含相同数量的元素)。如果你有不同 POD 不同长度的向量,你必须在写入原始数据之前将向量中元素的数量写入文件。

此外,当您在不同系统之间以二进制形式传输数字类型时,请注意endianness

【讨论】:

    【解决方案2】:

    您正在写入矢量结构文件,而不是其数据缓冲区。尝试将写入程序更改为:

     ofstream fout("data.dat", ios::out | ios::binary);
     fout.write((char*)&student[0], student.size() * sizeof(Student));
     fout.close();
    

    而不是从文件大小计算向量的大小,最好先写向量大小(对象数)。在这种情况下,您可以将其他数据写入同一文件。

     size_t size = student.size();
     fout.write((char*)&size, sizeof(size));
    

    【讨论】:

    • 你的意思是把sizeof(size)放在最后一行吗?
    • 具体应该是最后一行的sizeof(size_t),而不是sizeof(size)
    【解决方案3】:

    您可能无法以二进制(您正在做的方式)写入任何std::vector,因为该模板包含内部指针,写入和重新读取它们是没有意义的。

    一些一般性建议:

    • 不要以二进制形式编写任何 STL 模板容器(如 std::vectorstd::map),它们肯定包含您真的不想按原样编写的内部指针。如果您确实需要编写它们,请实现自己的写入和读取例程(例如使用 STL 迭代器)。

    • 避免随意使用strcpy。如果名称超过 30 个字符,您的代码将崩溃。至少,请使用strncpy(m_name, name, sizeof(m_name));(但即使这样对于 30 个字符的名称也不会奏效)。实际上,m_name 应该是 std::string

    • 显式序列化您的容器类(通过处理每个有意义的成员数据)。您可以考虑使用JSON 表示法(或者可能是YAML,或者甚至是XML——我觉得这太复杂所以不推荐)来序列化。它为您提供文本转储格式,您可以使用标准编辑器轻松检查(例如emacsgedit)。你会发现很多序列化的免费库,例如jsoncpp 和许多其他人。

    • 学习使用g++ -Wall -g 编译并使用gdb 调试器和valgrind 内存泄漏检测器;也学会使用make和写你的Makefile-s。

    • 利用 Linux 是免费软件的优势,因此您可以查看它的源代码(即使 STL 标头很复杂,您也可能想研究 stdc++ 实现)。

    【讨论】:

    • 好的,谢谢你的回答,但我能再问你一次吗?我曾在 Windows 中测试过这段代码,有时它可以工作,有时它会崩溃。
    • !那又怎样?您的程序有未定义的行为
    • 我猜你是对的,也许wind*ws的内存管理和安全性没有linux那么严格。
    【解决方案4】:

    对于函数 read() 和 write(),您需要所谓的“普通旧数据”或“POD”。这基本上意味着类或结构内部必须没有指针,也不能有虚函数。 vector 的实现当然有指针——我不确定虚函数。

    您必须编写一个函数来一次存储一个学生(或者将一群学生转换为字节数组 [不是向量] 或类似的数组 - 但这更复杂)。

    您不能将非 POD 数据(尤其是指针)写入二进制文件的原因是,当您再次读回数据时,几乎可以肯定的是,内存布局与您写入时相比已经发生了变化。这有点像试图在商店的同一个停车位停车 - 当您下次出现时,其他人将停在入口的第三个位置,因此您必须选择另一个位置。将编译器分配的内存视为停车位,将学生信息视为汽车。

    [从技术上讲,在这种情况下,情况更糟 - 你的向量实际上并不包含班级内的学生,这就是你正在写入文件的内容,所以你甚至没有保存有关学生的信息,只是关于他们所在位置的信息(停车位的数量)]

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-15
      • 2021-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多