加载和保存矢量到文件

Posted

技术标签:

【中文标题】加载和保存矢量到文件【英文标题】:Loading and Saving vectors to a file 【发布时间】:2012-09-13 23:23:09 【问题描述】:

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

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

【问题讨论】:

你遇到了什么麻烦?你知道如何加载/保存单个Appointment吗? 序列化可能是必要的,特别是如果您的Appointment1 包含非平凡的类型(如std::string,甚至只是您自己分配的动态内存)。也就是说,boost::serialization 可以很好,但无论如何都没有必要。 我认为您不需要为此进行提升,只需为每种对象类型进行序列化的例程即可。 (有效保存就是序列化) @MooingDuck 我同意!但似乎有一些人不喜欢听到这个。 =) 【参考方案1】:

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

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

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

[编辑]

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

所以如果你只需要序列化几个类型并且还没有使用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);

【讨论】:

嗯...只要Appointment 中没有包含重要的类型就可以了,但如果不是这样就行不通。如果约会的名称存储在std::string 中怎么办? 我的观点是,如果你不经常使用 boost,它会很痛苦。引入它来解决一个简单的问题通常不是最明智的解决方案。没有什么可以阻止您制作一些非常简单的支持函数来序列化您的向量(和字符串等)。然后,您只需为Appointment 创建一个readwrite 函数,您的完成速度比编译boost 快。代码更容易理解,根据我的经验,这种方法将在未来得到赞赏。个人观点,但来自实践经验。为反对票干杯 =) 编辑您的答案以包含该解释,我很乐意删除反对票。答案似乎暗示它就像outfile.write(appointments.data(), appointments.size() * sizeof(Appointment)); 一样简单,我认为我们可以同意这是不正确的。 当然。实际上这个问题意味着有一个头被写出(而不是写出Appointment对象),表明OP已经理解了平凡和非平凡类型之间的区别,只是想知道它是否值得使用火箭筒摧毁一只蚂蚁。 =) @paddy:谢谢,我刚刚将我当前的代码添加到我的帖子中。你能看一下吗。稍后,当我在我的机器上运行代码时,我会查看你的代码。最好,我想让我的代码正常工作。如果这不可能,我会更详细地查看您的示例。【参考方案2】:

序列化是更好的方法吗?

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

我认为您最好使用序列化库。此时您对库的使用可能会受到限制,但如果您的应用程序增长...C++ Middleware Writer 是传统序列化库的在线替代品。

【讨论】:

以上是关于加载和保存矢量到文件的主要内容,如果未能解决你的问题,请参考以下文章

将矢量并行保存到文件

将重 SVG(大约 10k 矢量对象)加载到画布中

最佳模型的 GridSearch:保存和加载参数

矢量化后,讲文件保存为SHP格式后,导入ARCMAP中,怎样能够定义坐标?

时间循环 - 将矢量数据存储在文件中而不覆盖

将信息保存到文件中并在启动时加载?