我想把一个包含不同数据类型的类的实例写入硬盘,并在需要时读取它。我使用了以下代码来完成此操作。问题是,每当我将对象保存到文件中时,它都会在文件夹上创建一个文件,但大小只有1KB。此外,当我从保存文件的同一函数打开文件时,我可以读取类中的变量,但当我将读取部分移动到另一个函数并从那里打开文件时时,变量无法读取。我该如何解决这个问题?提前谢谢。
写入文件:
stream.open("configuration/KMeansModel.bin", std::ios::out | std::ios::binary);
stream.write((char *)& kmeans, sizeof(kmeans));
stream.close();
从文件中读取:
KMeans::KMeans kmeans_(umapFeatureLabel_);
stream_.open("configuration/KMeansModel.bin", std::ios::in, std::ios::binary);
stream_.read((char *)& kmeans_, sizeof(kmeans_));
stream_.close();
类别定义:
class KMeans
{
private:
int m_K;
int m_iters;
int m_dimensions;
int m_total_features;
std::vector<Cluster> m_clusters;
std::unordered_map<std::string, std::string> m_umapFeatureLabel;
std::unordered_map<int, std::vector<std::vector<long double>>> m_umapClusterFeatureList;
int getNearestClusterId(Feature feature);
public:
KMeans::KMeans::KMeans();
KMeans(std::unordered_map<std::string, std::string>& umapFeatureLabel);
void run(std::vector<Feature>& allFeatures);
void predict(Feature feature);
void updateKMeans(std::vector<Feature>& allNewFeaturesRead);
std::string getLabelOfFeature(std::string feature);
};
坏消息:
文件保存代码使用函数sizeof
。您的数据结构包括矢量和地图对象。
例如,就sizeof
而言,std::vector
对象占用16个字节,而与元素数量无关。假设是64位机器,那么元素计数是8字节,加上指向实际元素的指针是8字节。
假设你的向量有100个元素,每个元素有8个字节,这些元素从内存地址424000开始存储。write
方法将尽职尽责地将a(数字100和b(数字424000存储到文件中;但是它绝对不会尝试保存到从424000到424800的文件存储器位置中。因为它无法知道424000是一个指针;这只是一个数字。
因此,该文件不包含恢复矢量状态所需的信息。
如以上评论中所述,将基于复杂指针的数据结构保存到简单字节数组中以用于文件存储或网络传输的主题被称为序列化或编组/解编组。
它本身是一个不明显的主题,就像排序算法或矩阵乘法是不明显的科目一样。您可能需要花费大量时间才能找到一个经过适当调试的解决方案,一个能够维护保存和恢复代码之间一致性的解决方案等等。。。
好消息是:
序列化是一个不明显的主题,但它也是一个古老的、众所周知的主题。因此,您可以依赖现有的、公开可用的代码,而不是痛苦地提出自己的解决方案。
以类似的方式,你必须想出自己的矩阵乘法代码的唯一情况是:
- 你这样做纯粹是为了好玩和/或自我训练
- 你正在写一篇关于矩阵乘法的博士论文
- 你是花钱写线性代数代码的
除此之外,您可能会依赖现有的LAPACK代码。
关于序列化,正如Botje在上面的评论中暗示的那样,Boost网站提供了一个C++序列化库,以及一个合适的教程。
示例代码:
我在下面提供了一个使用Boost库的小代码示例。一个简单的豚鼠对象包含一个整数值、一个字符串和一个映射。当然,我无耻地借用了Boost教程。
我们需要包括几个头文件:
#include <map>
#include <fstream>
#include <iostream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/map.hpp>
对象类,它假装存储一些象征性的地理信息:
class CapitalMap
{
public:
CapitalMap(const std::string& myName, int myVersion) :
_name(myName), _version(myVersion)
{};
CapitalMap() = default; // seems required by serialization
inline void add(const std::string& country, const std::string& city)
{ _cmap[country] = city; }
void fdump(std::ostream& fh);
private:
std::string _name;
int _version;
std::map<std::string, std::string> _cmap;
friend class boost::serialization::access; // ALLOW FOR FILE ARCHIVAL
template<class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar & _name;
ar & _version; // mind the name conflict with plain "version" argument
ar & _cmap;
}
};
一个小型调试实用程序功能:
void CapitalMap::fdump(std::ostream& ofh) // text dumping utility for debug
{
ofh << "CapitalMap name = "" << _name << "" version = " <<
_version << 'n';
for (const auto& pair : _cmap) {
auto country = pair.first; auto city = pair.second;
ofh << city << " is the capital of " << country << 'n';
}
}
创建对象、将其保存在磁盘上并(隐含地(解除分配的代码:
void buildAndSaveCapitalMap (const std::string& archiveName,
const std::string& mapName,
int version)
{
CapitalMap euroCapitals(mapName, version);
euroCapitals.add("Germany", "Berlin");
euroCapitals.add("France", "Paris");
euroCapitals.add("Spain", "Madrid");
euroCapitals.fdump(std::cout); // just for checking purposes
// save data to archive file:
std::ofstream ofs(archiveName);
boost::archive::text_oarchive oa(ofs);
oa << euroCapitals;
// ofstream connexion closed automatically here
// archive object deleted here - because going out of scope
// CapitalMap object deleted here - because going out of scope
}
创建文件,然后从该文件恢复对象状态的小型主程序:
int main(int argc, char* argv[])
{
const std::string archiveName{"capitals.dat"};
std::cout << std::endl;
buildAndSaveCapitalMap(archiveName, "EuroCapitals", 42);
// go restore our CapitalMap object to its original state:
CapitalMap cm; // object created in its default state
std::ifstream ifs(archiveName);
boost::archive::text_iarchive inAr(ifs);
inAr >> cm; // read back object ...
std::cout << std::endl;
cm.fdump(std::cout); // check that it's actually back and in good shape ...
std::cout << std::endl;
return 0;
}
通过根据行进方向更改运算符"&"的含义,可以出色地解决保存和恢复代码之间保持一致性的问题。
一路上的小问题:
- 在Linux发行版上,您需要获得以下软件包:boost、boost-devel、boost序列化
- 对象类似乎需要有一个默认构造函数
- 您需要手动包含诸如"boost/serialization/map.hpp"之类的文件
程序执行:
$ g++ serialw00.cpp -lboost_serialization -o ./serialw00.x
$ ./serialw00.x
CapitalMap name = "EuroCapitals" version = 42
Paris is the capital of France
Berlin is the capital of Germany
Madrid is the capital of Spain
CapitalMap name = "EuroCapitals" version = 42
Paris is the capital of France
Berlin is the capital of Germany
Madrid is the capital of Spain
$
更多详情请点击:SO_q_523872