Подтвердить что ты не робот

Как правильно писать вектор в двоичный файл в С++?

спасибо Мацу Петерсону за объяснение, чтобы скопировать вектор в массив, кажется, он работает. здесь код snipset:

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

но я гость этот метод будет тратить огромную память и громоздко. возможно, я подумаю о написании этого г-на с оцелотом и другим предложением. спасибо всем за ответ.

4b9b3361

Ответ 1

Вы пишете файл векторной структуры, а не буфер данных. Попробуйте изменить процедуру записи:

 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));

Ответ 2

Чтобы сохранить в файле vector<T> POD, вы должны написать содержимое вектора, а не сам вектор. Вы можете получить доступ к необработанным данным с помощью &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.

Ответ 3

Вероятно, вы не можете записать в двоичном виде (как вы это делаете) любой std::vector, потому что этот шаблон содержит внутренние указатели, а запись и повторное чтение их бессмысленны.

Некоторые общие советы:

  • не записывать в двоичные любые контейнеры шаблонов STL (например, std::vector или std::map), они обязательно содержат внутренние указатели, которые вы действительно не хотите писать как есть. Если вам действительно нужно написать их, выполните свои собственные процедуры написания и чтения (например, с помощью итераторов STL).

  • не используйте strcpy без особого внимания. Ваш код сработает, если имя имеет более 30 символов. По крайней мере, используйте strncpy(m_name, name, sizeof(m_name)); (но даже это плохо работает для имени 30 символов). Фактически, m_name должен быть std::string.

  • сериализуйте явно ваши классы контейнеров (обработайте все значимые данные элемента). Вы можете использовать JSON нотацию (или, возможно, YAML или, может быть, даже XML, который я считаю слишком сложным, поэтому не рекомендую сериализовать. Он дает текстовый формат дампа, который вы можете легко проверить стандартным редактором (например, emacs или gedit). Вы найдете много сериализованных бесплатных библиотек, например. jsoncpp и многие другие.

  • научиться компилироваться с помощью g++ -Wall -g и использовать отладчик gdb и детектор утечки памяти valgrind; также научитесь использовать make и написать свой Makefile -s.

  • воспользуйтесь преимуществами бесплатного программного обеспечения Linux, поэтому вы можете изучить его исходный код (и вы можете изучить реализацию stdС++, даже если заголовки STL сложны).

Ответ 4

Для функций read() и write() вам нужны так называемые "простые старые данные" или "POD". Это означает, что в основном класс или структура не должны содержать указателей внутри них, а не виртуальных функций. реализация вектора, безусловно, имеет указатели - я не уверен в виртуальных функциях.

Вам нужно будет написать функцию, которая хранит ученика за раз (или это переводит кучу студентов в массив [не вектор] байтов или какой-то такой, но более сложный).

Причина, по которой вы не можете записывать данные, отличные от POD, в частности указатели, в двоичный файл, заключается в том, что когда вы снова читаете данные, вы почти наверняка можете утверждать, что макет памяти изменился с момента написания. Это становится немного похоже на попытку припарковаться на одном и том же парковочном пространстве в магазинах - кто-то еще припаркован в третьем месте от входа, когда вы появляетесь в следующий раз, так что вам нужно будет выбрать другое место. Подумайте о памяти, выделенной компилятором в качестве парковочных мест, а информация о студенте как автомобили.

[Технически, в этом случае это еще хуже - ваш вектор на самом деле не содержит студентов внутри класса, что вы пишете в файл, поэтому вы даже не сохранили информацию о студентах, просто информация о том, где они расположены (номер парковочных мест)]