Loading and Saving vectors to a file

匿名 (未验证) 提交于 2019-12-03 08:30:34

问题:

I am using C++ Builder and I have a vector array of Appointment objects.

I want to save it to and load it from a file.

Currently, I am using ifstream and ofstream with binary files. I have a header that contains the size of the vector that will be saved alongside the data, so as to know its size when loading.

Is serilization a better way to do this?

If so, do I need to use the boost library, or another way?

Here is my current code:

class appointment { public:     appointment();     appointment(TDateTime aDate, TDateTime aReminderDateTime, string aType,     string aLocation, string aComments, bool aIsImportant)     {         appDateTime = aDate;         appReminderDateTime = aReminderDateTime;         appType = aType;         appLocation = aLocation;         appComments = aComments;         appIsImportant = aIsImportant;     }     void setAppDateTime(TDateTime aDateTime)     {         appDateTime = aDateTime;     }     void setappReminderDateTime(TDateTime aReminderDateTime)     {         appReminderDateTime = aReminderDateTime;     }     /*     void printAppointmentDetails()     {         cout << "Appointment Date: " << appDateTime << endl;         cout << "Appointment Reminder Date: " << appReminderDateTime << endl;         cout << "Appointment Type: " << appType << endl;         cout << "Appointment Location: " << appLocation << endl;         cout << "Appointment Comments: " << appComments << endl;         if (appIsImportant)         {             cout << "Appointment IsImportant: " << "Yes" << endl;         } else {             cout << "Appointment IsImportant: " << "No" << endl;         }     }      */     void setType(string aType)     {         appType = aType;     }     void setLocation(string aLocation)     {         appLocation = aLocation;     }     void setComments(string aComments)     {         appComments = aComments;     }     void setIsImportant(bool aIsImportant)     {         appIsImportant = aIsImportant;     }     TDateTime getAppDateTime()     {         return appDateTime;     }     TDateTime getAppReminderDateTime()     {         return appReminderDateTime;     }     string getType()     {         return appType;     }     string getLocation()     {         return appLocation;     }     string getComments()     {         return appComments;     }     bool getIsImportant()     {         return appIsImportant;     } private:     //appointment();     TDateTime appDateTime;     TDateTime appReminderDateTime;     string appType;     string appLocation;     string appComments;     bool appIsImportant;     //person owner; };  class calendar { public:     calendar()     {         //loadFromFile();         //load persons         //calculateimportantAppointments     }     ~calendar()     {         saveToFile();     }     //addperson     //editperson     //removeperson     void createAppointment(TDateTime aDate, TDateTime aReminderDateTime, string aType,     string aLocation, string aComments, bool aIsImportant)     {         appointment newAppointment(aDate, aReminderDateTime, aType,         aLocation, aComments, aIsImportant);         appointments.push_back(newAppointment);     }     /*     void printAllAppointmentDetails()     {         for (int i = 0; i < appointments.size(); i++)         {             appointments[i].printAppointmentDetails();         }     }     void calculateImportantAppointments()     {      }     int getNumberOfImportantAppointments()     {         int intImportantAppointmentCount = 0;         for (int i = 0; i < appointments.size(); i++)         {              if (appointments[i].getIsImportant())                 intImportantAppointmentCount += 1;         }         return intImportantAppointmentCount;     }      appointment[] getImportantAppointments()     {      }     appointment[] getAllAppointments()     {      }     */     void loadFromFile()     {         ifstream iStream("file.ext", ios::binary);         if (!iStream)         {             cout << "No file";         } else {             fileHeader_t fHeader;             iStream.read((char*)&fHeader, sizeof(fileHeader_t));             if (fHeader.magicNumber = 0xDEADBEAF)             {                 appointments.resize(fHeader.appointmentCount);                 iStream.read((char*)&appointments[0], fHeader.appointmentCount * sizeof(appointment));             }         }     }     void saveToFile()     {         ofstream oStream("file.ext", ios::binary);         fileHeader_t fHeader;         fHeader.magicNumber = 0xDEADBEAF;         fHeader.appointmentCount = appointments.size();         oStream.write((char*)&fHeader, sizeof(fileHeader_t));         oStream.write((char*)&appointments[0], sizeof(appointment) * appointments.size());     }     //vector<appointment> appointments; private:     vector<appointment> appointments;     string calCurrentDate;     string calCurrentTime;     typedef struct fileHeader_s     {         DWORD magicNumber;         size_t appointmentCount;     }fileHeader_t; }; 

I am getting thew following errors when calling the loadFromFile() method.

[BCC32 Warning] File1.cpp(185): W8060 Possibly incorrect assignment [ILINK32 Error] Error: Unresolved external 'appointment::appointment()' referenced from \PROFILES.SOIT.LOCAL\HOMES$\SIMON.CANNING\MY DOCUMENTS\RAD STUDIO\PROJECTS\DEBUG\FILE1.OBJ [ILINK32 Error] Error: Unable to perform link

I understand that this happens because of the constructor call. Can I please have some advice on how to fix this issue?

回答1:

With all the dramas you may have getting boost to compile, and then all the guff you have to do to implement serialization, I personally don't bother.

Just set the size into your header, write it out to file, then write out your vector's bytes.

When loading, read in the header, resize the vector to what it says, and then read in the vector's bytes.

[edit]

As discussed in the comments, you must be aware that you can't write out other non-trivial types (such as strings) as binary either. All these must be serialized. I had inferred, from the way you posed your question, that you were already aware of this.

So if you only need to serialize a few types and don't already use boost, I personally believe that using boost to solve this problem will be overkill. People seem to have responded negatively to the way in which I expressed this opinion, so maybe they have never had to deal with a project where somebody built in a dependence on boost serialization to solve a really simple and isolated problem =)

What you really need is a handful of simple support functions which you can write yourself. You don't even really need that header to contain the vector size in this case because you can serialize...

// This writes a vector of trivial data types. template <class T> void WriteTrivial( std::ostream& s, const std::vector<T>& data ) {     unsigned int len = data.size();     s.write( (char*)&len, sizeof(len) );     s.write( (const char*)&data[0], len * sizeof(T) ); }  // This reads a vector of trivial data types. template <class T> void ReadTrivial( std::istream& s, std::vector<T>& data ) {     unsigned int len = 0;     s.read( (char*)&len, sizeof(len) );     data.resize(len);     if( len > 0 ) s.read( (char*)&data[0], len * sizeof(T) ); } 

If your vector might contain strings or vectors, you need a few more support functions

// This writes a vector of non-trivial data types. template <class T> void Write( std::ostream& s, const std::vector<T>& data ) {     unsigned int len = data.size();     s.write( (char*)&len, sizeof(len) );     for( unsigned int i = 0; i < len; i++ ) {         Write( s, data[i] );     } }  // This reads a vector of non-trivial data types. template <class T> void Read( std::istream& s, std::vector<T>& data ) {     unsigned int len = 0;     s.read( (char*)&len, sizeof(len) );     data.resize(len);     for( unsigned int i = 0; i < len; i++ ) {         Read( s, data[i] );     }  } 

And of course with the above you need something for strings, and a Read/Write template to handle normal data types. This should get you started anyway. Hope that helps.

[edit]

Now that you have posted your code, I suggest this:

In Calendar:

void loadFromFile() {     ifstream iStream("file.ext", ios::binary);     if (!iStream)     {         cout << "No file";     } else {         fileHeader_t fHeader;         iStream.read((char*)&fHeader, sizeof(fileHeader_t));         if (fHeader.magicNumber != 0xDEADBEAF) return;         appointments.resize(fHeader.appointmentCount);         for( size_t i = 0; i < appointments.size(); i++ ) {                         appointments[i].read(iStream);         }         iStream.close();     } }  void saveToFile() {     ofstream oStream("file.ext", ios::binary);     fileHeader_t fHeader;     fHeader.magicNumber = 0xDEADBEAF;     fHeader.appointmentCount = appointments.size();     oStream.write((char*)&fHeader, sizeof(fileHeader_t));     for( size_t i = 0; i < appointments.size(); i++ ) {                     appointments[i].write(oStream);     }     oStream.close(); } 

Now, for serialising strings:

void write( ostream &s, const string& str ) {     unsigned int len = str.size();     s.write((char*)&len, sizeof(len));     s.write(str.c_str(), len*sizeof(char)); }  void read( istream &s, string& str ) {     unsigned int len = 0;     s.read((char*)&len, sizeof(len));     str.resize(len);     if( len == 0 ) return;     s.read((char *) str.c_str(), len*sizeof(char)); } 

And maybe a helpful wrapper for writing trivial types:

template <class T> void writeTrivial( ostream& s, const T& val ) {     ostream.write( (const char*)&val, sizeof(T) ); }  template <class T> void readTrivial( ostream& s, T& val ) {     ostream.read( (char*)&val, sizeof(T) ); } 

And finally, in Appointment

void write( ostream& s ) {     writeTrivial(s, appDateTime);     writeTrivial(s, appReminderDateTime);     write(s, appType);     write(s, appLocation);     write(s, appComments);     writeTrivial(s, appIsImportant); }  void read( istream& s ) {     readTrivial(s, appDateTime);     readTrivial(s, appReminderDateTime);     read(s, appType);     read(s, appLocation);     read(s, appComments);     readTrivial(s, appIsImportant); } 


回答2:

Is serilization a better way to do this?

If so, do I need to use the boost library, or another way?

I think you'd be better off using a serialization library. Your use of a library might be limited at this point, but if your application grows... The C++ Middleware Writer is an on line alternative to traditional serialization libraries.



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!