1

我正在使用 C++ Builder,并且我有一个Appointment对象向量数组。

我想将其保存到文件中并从文件中加载。

目前,我将 ifstream 和 ofstream 与二进制文件一起使用。我有一个标题,其中包含将与数据一起保存的向量的大小,以便在加载时知道它的大小。

序列化是更好的方法吗?

如果是这样,我需要使用 boost 库还是其他方式?

这是我当前的代码:

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\MY DOCUMENTS\ 引用的未解决的外部 'appointment::appointment()' RAD STUDIO\PROJECTS\DEBUG\FILE1.OBJ [ILINK32 错误] 错误:无法执行链接

我知道这是由于构造函数调用而发生的。我可以就如何解决此问题提供一些建议吗?

4

2 回答 2

2

有了所有你可能需要编译的戏剧,然后你必须做的所有废话来实现序列化,我个人并不费心。

只需将大小设置到标题中,将其写入文件,然后写出向量的字节。

加载时,读入标头,将向量调整为它所说的大小,然后读入向量的字节。

[编辑]

正如评论中所讨论的,您必须意识到您也不能将其他非平凡类型(例如字符串)写成二进制。所有这些都必须序列化。根据您提出问题的方式,我推断您已经意识到这一点。

所以如果你只需要序列化几个类型并且还没有使用boost的话,我个人认为使用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);
}
于 2012-09-13T23:58:40.547 回答
0

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.

于 2012-09-14T15:47:20.643 回答