缘由
最近在跑实验的时候,遇到一个需求,就是将一个vector<Eigen::VectorXd>的对象存到本地文件中,然后在需要使用的时候,再读取出来。
由于感觉不是很难,所以没有到网上找资料自己写,直接喂给了chatgpt。然后chatgpt给我返回了下面的代码。
Chatgpt版本代码
void saveToFile(const std::vector<Eigen::VectorXd>& data, const std::string& filename) {
std::ofstream outFile(filename);
if (!outFile) {
std::cerr << "Error opening file for writing: " << filename << std::endl;
return;
}
outFile << data.size() << std::endl;
for (const auto& vec : data) {
outFile << vec.size() << std::endl;
for (int i = 0; i < vec.size(); ++i) {
outFile << vec(i) << (i < vec.size() - 1 ? " " : ""); // 使用空格分隔
}
outFile << std::endl; // 每个向量后换行
}
outFile.close();
}
std::vector<Eigen::VectorXd> loadFromFile(const std::string& filename) {
std::ifstream inFile(filename);
if (!inFile) {
std::cerr << "Error opening file for reading: " << filename << std::endl;
return {};
}
std::vector<Eigen::VectorXd> data;
size_t numVectors;
inFile >> numVectors;
for (size_t i = 0; i < numVectors; ++i) {
size_t vecSize;
inFile >> vecSize;
Eigen::VectorXd vec(vecSize);
for (size_t j = 0; j < vecSize; ++j) {
inFile >> vec(j);
}
data.push_back(vec);
}
inFile.close();
return data;
}
咋一看感觉确实没毛病,写了一个简单的测试函数,输入常规的数据,发现存取的数据是”肉眼“一致的。(记住这里的”肉眼“一致)
看起来没啥问题就用了,但是我发现实验结果总是不太对,和正常的结果差了不少。本来是没怀疑这两个函数的,毕竟”肉眼“一致的。但是我仔仔细细查了其它部分的代码,虽然找到了一些数据读取上的bug,但是改了之后还是没有正常。
本来打算放弃不找了,但是又想试试不存取数据的方案,然后就发现不存取数据,结果正常了。这个时候我就认定上面的两个函数有问题了。
可以,它两”肉眼“正确,我也没多怀疑。不过,我的师弟知道这件事情后来了兴致,想要好好看看。经过师弟的一番折腾,还真发现了问题。
师弟的代码(正确的代码)
void saveToFileV2(const std::vector<Eigen::VectorXd>& data, const std::string& filename) {
std::ofstream outFile(filename,std::ios::binary);
if (!outFile) {
std::cerr << "Error opening file for writing: " << filename << std::endl;
return;
}
size_t numVectors = data.size();
outFile.write(reinterpret_cast <const char*> (&numVectors),sizeof (numVectors));
for (const auto& vec : data) {
size_t vecSize = vec.size();
outFile.write(reinterpret_cast <const char*> (&vecSize),sizeof (vecSize));
for (int i = 0; i < vec.size(); ++i) {
outFile.write(reinterpret_cast <const char*> (&vec[i]),sizeof (double));
// outFile << vec(i) << (i < vec.size() - 1 ? " " : ""); // 使用空格分隔
}
// outFile << std::endl; // 每个向量后换行
// outFile.write(reinterpret_cast <const char*> (vec.data()),vecSize * sizeof (double));
}
outFile.close();
}
std::vector<Eigen::VectorXd> loadFromFileV2(const std::string& filename) {
std::ifstream inFile(filename,std::ios::binary);
if (!inFile) {
std::cerr << "Error opening file for reading: " << filename << std::endl;
return {};
}
std::vector<Eigen::VectorXd> data;
size_t numVectors;
// inFile >> numVectors;
inFile.read(reinterpret_cast<char *>(&numVectors),sizeof (numVectors));
for (size_t i = 0; i < numVectors; ++i) {
size_t vecSize;
// inFile >> vecSize;
inFile.read(reinterpret_cast<char *>(&vecSize),sizeof (vecSize));
Eigen::VectorXd vec(vecSize);
// inFile.read(reinterpret_cast<char *>(vec.data()),vecSize * sizeof (double ));
for (size_t j = 0; j < vecSize; ++j) {
// inFile >> vec(j);
inFile.read(reinterpret_cast <char*> (&vec[j]),sizeof (double));
}
data.push_back(vec);
}
inFile.close();
return data;
}
师弟说我之前的代码存的数据是以文本形式存储,对于高精度的数据来说,在存取的过程中可能损失精度。所以,他改了存储方式,将数据以二进制形式存储,这样的话,精度就不会损失,可以保证每一位都一致。
师弟说得很有道理,对于高精度数据来说,必须要用二进制形式存储,否则的话,精度损失很严重。不过,如果精度要求比较低或者说是常规的整数数据,还是可以用原来的代码存取。
总结
对于存取vector<Eigen::VectorXd>类型的数据,还是建议用后面版本的代码。同时,如果说是其它类型的数据,也推荐存为二进制,而不是文本,否则可能出现问题。
最后,感谢师弟的钻研精神,也感谢师弟帮我远程配置实验环境!