Я использую C ++ Builder и у меня есть векторный массив Appointment
объекты.
Я хочу сохранить его и загрузить из файла.
В настоящее время я использую ifstream и ofstream с двоичными файлами. У меня есть заголовок, который содержит размер вектора, который будет сохранен вместе с данными, чтобы знать его размер при загрузке.
Является ли сериализация лучшим способом сделать это?
Если так, нужно ли мне использовать библиотеку наддува или другим способом?
Вот мой текущий код:
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;
};
Я получаю следующие ошибки при вызове метода loadFromFile ().
[BCC32 Предупреждение] File1.cpp (185): W8060 Возможно неправильное назначение
[ILINK32 Ошибка] Ошибка: Неразрешенная внешняя функция «назначение :: назначение ()», на которую ссылается \ PROFILES.SOIT.LOCAL \ HOMES $ \ SIMON.CANNING \ МОИ ДОКУМЕНТЫ \ RAD STUDIO \ ПРОЕКТЫ \ DEBUG \ FILE1.OBJ
[Ошибка ILINK32] Ошибка: невозможно выполнить ссылку
Я понимаю, что это происходит из-за вызова конструктора. Могу ли я дать несколько советов, как решить эту проблему?
Со всеми драмами, которые вы можете получить, чтобы получить компиляцию, а затем со всем, что вам нужно сделать для реализации сериализации, я лично не беспокоюсь.
Просто установите размер в свой заголовок, запишите его в файл, затем запишите байты вашего вектора.
При загрузке прочитайте в заголовке, измените размер вектора на то, что он говорит, а затем прочитайте в байтах вектора.
[редактировать]
Как уже говорилось в комментариях, вы должны знать, что вы не можете записывать другие нетривиальные типы (например, строки) в двоичном виде. Все это должно быть сериализовано. По тому, как вы задали свой вопрос, я понял, что вы уже знали об этом.
Поэтому, если вам нужно только сериализовать несколько типов и еще не использовать boost, я лично считаю, что использование boost для решения этой проблемы будет излишним. Люди, похоже, негативно отреагировали на то, как я выразил это мнение, поэтому, возможно, им никогда не приходилось иметь дело с проектом, в котором кто-то строил зависимость от ускоренной сериализации, чтобы решить действительно простую и изолированную проблему =)
Что вам действительно нужно, так это несколько простых функций поддержки, которые вы можете написать сами. В этом случае вам даже не нужен этот заголовок, чтобы он содержал размер вектора, потому что вы можете сериализовать …
// 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) );
}
Если ваш вектор может содержать строки или векторы, вам нужно еще несколько вспомогательных функций
// 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] );
}
}
И, конечно, с учетом вышесказанного вам нужно что-то для строк и шаблон чтения / записи для обработки обычных типов данных. В любом случае это должно помочь вам начать. Надеюсь, это поможет.
[редактировать]
Теперь, когда вы опубликовали свой код, я предлагаю следующее:
В Календарь:
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();
}
Теперь для сериализации строк:
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));
}
И, возможно, полезная оболочка для написания тривиальных типов:
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) );
}
И, наконец, в Деловое свидание, встреча
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);
}
Является ли сериализация лучшим способом сделать это?
Если так, нужно ли мне использовать библиотеку наддува или другим способом?
Я думаю, вам лучше использовать библиотеку сериализации. На этом этапе ваше использование библиотеки может быть ограничено, но если ваше приложение растет … C ++ Middleware Writer это онлайновая альтернатива традиционным библиотекам сериализации.