加载和保存矢量到文件

2022-01-24 00:00:00 serialization save load c++ c++builder

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

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.

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

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?

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

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

这是我当前的代码:

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() 方法时出现以下错误.

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

[BCC32 警告] File1.cpp(185): W8060 可能分配不正确[ILINK32 错误] 错误:从 PROFILES.SOIT.LOCALHOMES$SIMON.CANNINGMY DOCUMENTSRAD STUDIOPROJECTSDEBUGFILE1.OBJ 引用的未解析的外部 'appointment::appointment()'[ILINK32 错误] 错误:无法执行链接

[BCC32 Warning] File1.cpp(185): W8060 Possibly incorrect assignment [ILINK32 Error] Error: Unresolved external 'appointment::appointment()' referenced from PROFILES.SOIT.LOCALHOMES$SIMON.CANNINGMY DOCUMENTSRAD STUDIOPROJECTSDEBUGFILE1.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?

推荐答案

你可能要编译的所有戏剧,然后你必须做的所有花言巧语来实现序列化,我个人并不费心.

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.

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

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.

所以如果你只需要序列化几个类型并且还没有使用boost的话,我个人认为使用boost来解决这个问题会有点矫枉过正.人们似乎对我表达这种观点的方式做出了负面反应,所以也许他们从来不需要处理一个项目,其中有人依赖于 boost 序列化来解决一个非常简单和孤立的问题 =)

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.

既然您已经发布了代码,我建议您这样做:

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

在日历中:

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

也许是编写琐碎类型的有用包装器:

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

最后,预约

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

相关文章