简体   繁体   中英

how to correctly write vector to binary file in c++?

Thanks to Mats Petersson for the explanation on how to to copy vector to array, it's seems work. Here is the code snipet:

#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;
      }

But I guest this method will waste huge memory and be cumbersome. Maybe I will consider writing it Mr. with ocelot and other suggestions. Thanks everyone for the answer.

To store a vector<T> of PODs in a file, you have to write the contents of the vector, not the vector itself. You can access the raw data with &vector[0] , address of the first element (given it contains at least one element). To get the raw data length, multiply the number of elements in the vector with the size of one element:

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

The same applies when you read the vector from the file; The element count is the total file size divided by the size of one element (given that you only store one type of POD in the file):

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

This only works if you can calculate the number of elements based on the file size (if you only store one type of POD or if all vectors contain the same number of elements). If you have vectors with different PODs with different lengths, you have to write the number of elements in the vector to the file before writing the raw data.

Furthermore, when you transfer numeric types in binary form between different systems, be aware of endianness .

You are writing to file the vector structure, not its data buffer. Try change writing procedure to:

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

And instead of calculation size of vector from file size, it's better write vector size (number of objects) before. In the case you can write to the same file other data.

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

You probably cannot write in binary (the way you are doing) any std::vector because that template contains internal pointers, and writing and re-reading them is meaningless.

Some general advices:

  • don't write in binary any STL template containers (like std::vector or std::map ), they surely contain internal pointers that you really don't want to write as is. If you really need to write them, implement your own writing and reading routines (eg using STL iterators).

  • avoid using strcpy without care. Your code will crash if the name has more than 30 characters. At least, use strncpy(m_name, name, sizeof(m_name)); (but even that would work badly for a 30 characters name). Actually, m_name should be a std::string .

  • serialize explicitly your container classes (by handling each meaningful member data). You could consider using JSON notation (or perhaps YAML , or maybe even XML -which I find too complex so don't recommend) to serialize. It gives you a textual dump format, which you could easily inspect with a standard editor (eg emacs or gedit ). You'll find a lot of serializing free libraries, eg jsoncpp and many others.

  • learn to compile with g++ -Wall -g and to use the gdb debugger and the valgrind memory leakage detector; also learn to use make and to write your Makefile -s.

  • take advantage that Linux is free software, so you can look into its source code (and you may want to study stdc++ implementation even if the STL headers are complex).

For functions read() and write(), you need what is called "plain old data" or "POD". That means basically that the class or structure must have no pointers inside them, and no virtual functions. the implementation of vector certainly has pointers - I'm not sure about virtual functions.

You will have to write a function that stores a student at a time (or that translates a bunch of students to a array [not a vector] of bytes or some such - but that's more complex).

The reason you can't write non-POD data, in particular pointers, to a binary file is that when you read the data back again, you can almost certainly bet that the memory layout has changed from when you wrote it. It becomes a little bit like trying to park in the same parking space at the shops - someone else will have parked in the third spot from the entrance when you turn up next time, so you'll have to pick another spot. Think of the memory allocated by the compiler as parking spaces, and the student information as cars.

[Technically, in this case, it's even worse - your vector doesn't actually contain the students inside the class, which is what you are writing to the file, so you haven't even saved the information about the students, just the information about where they are located (the number of the parking spaces)]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM