web-dev-qa-db-fra.com

comment écrire correctement un vecteur dans un fichier binaire en c ++?

Merci à Mats Petersson pour l'explication sur la façon de copier un vecteur dans un tableau, cela semble fonctionner. Voici l'extrait de code:

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

Mais je pense que cette méthode gaspillera une énorme mémoire et sera lourde. Je vais peut-être envisager de l'écrire M. avec ocelot et d'autres suggestions. Merci à tous pour la réponse.

19
dchochan

Vous écrivez pour classer la structure vectorielle, pas son tampon de données. Essayez de modifier la procédure d'écriture pour:

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

Et au lieu de calculer la taille du vecteur à partir de la taille du fichier, il vaut mieux écrire la taille du vecteur (nombre d'objets) avant. Dans le cas où vous pouvez écrire dans le même fichier d'autres données.

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

Pour stocker un vector<T> of PODs dans un fichier, vous devez écrire le contenu du vecteur, pas le vecteur lui-même. Vous pouvez accéder aux données brutes avec &vector[0], adresse du premier élément (étant donné qu'il contient au moins un élément). Pour obtenir la longueur des données brutes, multipliez le nombre d'éléments dans le vecteur par la taille d'un élément:

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

La même chose s'applique lorsque vous lisez le vecteur du fichier; Le nombre d'éléments est la taille totale du fichier divisée par la taille d'un élément (étant donné que vous ne stockez qu'un seul type de POD dans le fichier):

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

Cela ne fonctionne que si vous pouvez calculer le nombre d'éléments en fonction de la taille du fichier (si vous ne stockez qu'un seul type de POD ou si tous les vecteurs contiennent le même nombre d'éléments). Si vous avez des vecteurs avec différents POD de différentes longueurs, vous devez écrire le nombre d'éléments dans le vecteur dans le fichier avant d'écrire les données brutes.

De plus, lorsque vous transférez des types numériques sous forme binaire entre différents systèmes, faites attention à endianness .

19
Anonymous Coward

Vous ne pouvez probablement pas écrire en binaire (comme vous le faites) aucun std::vector Car ce modèle contient des pointeurs internes, et les écrire et les relire n'a aucun sens.

Quelques conseils généraux:

  • n'écrivez en binaire aucun conteneur de modèle STL (comme std::vector ou std::map), ils contiennent sûrement des pointeurs internes que vous ne voulez vraiment pas écrire tels quels. Si vous avez vraiment besoin de les écrire, implémentez vos propres routines d'écriture et de lecture (par exemple en utilisant des itérateurs STL).

  • évitez d'utiliser strcpy sans précaution. Votre code plantera si le nom contient plus de 30 caractères. Au moins, utilisez strncpy(m_name, name, sizeof(m_name)); (mais même cela fonctionnerait mal pour un nom de 30 caractères). En fait, m_name Devrait être un std::string.

  • sérialiser explicitement vos classes de conteneur (en gérant chaque donnée membre significative). Vous pourriez envisager d'utiliser la notation JSON (ou peut-être YAML , ou peut-être même XML - que je trouve trop complexe donc je ne le recommande pas) pour sérialiser. Il vous donne un format de vidage textuel, que vous pouvez facilement inspecter avec un éditeur standard (par exemple emacs ou gedit). Vous trouverez beaucoup de sérialisation de bibliothèques gratuites, par exemple jsoncpp et bien d'autres.

  • apprendre à compiler avec g++ -Wall -g et à utiliser le débogueur gdb et le détecteur de fuite de mémoire valgrind; apprenez également à utiliser make et à écrire vos Makefile- s.

  • profitez du fait que Linux est un logiciel libre, vous pouvez donc consulter son code source (et vous voudrez peut-être étudier l'implémentation stdc ++ même si les en-têtes STL sont complexes).

3

Pour les fonctions read () et write (), vous avez besoin de ce qu'on appelle des "données anciennes simples" ou "POD". Cela signifie essentiellement que la classe ou la structure ne doit pas contenir de pointeurs ni de fonctions virtuelles. l'implémentation du vecteur a certainement des pointeurs - je ne suis pas sûr des fonctions virtuelles.

Vous devrez écrire une fonction qui stocke un étudiant à la fois (ou qui traduit un groupe d'élèves en un tableau [pas un vecteur] d'octets ou quelque chose comme ça - mais c'est plus complexe).

La raison pour laquelle vous ne pouvez pas écrire de données non POD, en particulier des pointeurs, dans un fichier binaire est que lorsque vous relisez les données, vous pouvez presque certainement parier que la disposition de la mémoire a changé par rapport à la date à laquelle vous les avez écrites. Cela ressemble un peu à essayer de se garer dans le même espace de stationnement dans les magasins - quelqu'un d'autre se sera garé au troisième endroit de l'entrée lorsque vous reviendrez la prochaine fois, vous devrez donc choisir un autre endroit. Considérez la mémoire allouée par le compilateur comme des espaces de stationnement et les informations des étudiants comme des voitures.

[Techniquement, dans ce cas, c'est encore pire - votre vecteur ne contient pas réellement les étudiants à l'intérieur de la classe, c'est ce que vous écrivez dans le fichier, donc vous n'avez même pas enregistré les informations sur les étudiants, juste les informations où ils se trouvent (le nombre de places de stationnement)]

3
Mats Petersson