从二进制文件加载列表后,我的代码似乎进入了无限循环



我正在研究STL,并决定编写一些代码来练习文件的编写和读取。该问题包括创建int列表(0,1,...,9),将其保存在二进制文件中,最后再次加载。

有 5 个基本代码块: 1. 创建列表 2. 礼物清单 3. 保存列表 4. 负载清单 5. 再次显示列表

这看起来简单明了;然而,代码似乎陷入了一个无限循环。

int main(){
list<int> numbers;
/////// Create list of 10 integers ///////
for(int i=0; i<10; i++){
numbers.push_back(i);
}
/////// Present List ///////
cout << "List created: [";
list<int>::iterator it;
for(it = numbers.begin(); it != numbers.end(); it++){
if(*it != 9){
cout << *it << ", ";
}
else{
cout << *it;
}
}
cout << "]" << endl;
/////// Save list ///////
string fileName = "test.bin";
ofstream outputFile;
outputFile.open(fileName, ios::binary);
if(outputFile.is_open()){
outputFile.write(reinterpret_cast<char *>(&numbers), sizeof(numbers));
outputFile.close();
cout << "List saved to file." << endl;
}
else{
cout << "Could not open file named " << fileName << endl;
}
/////// Load list ///////
list<int> anotherList;
ifstream inputFile;
inputFile.open(fileName, ios::binary);
if(inputFile.is_open()){
inputFile.read(reinterpret_cast<char *>(&anotherList), sizeof(anotherList));
inputFile.close();
cout << "List loaded from file." << endl;
}
else{
cout << "Could not open file named " << fileName << endl;
}

/////// Present List ///////
cout << "List loaded: [";
for(it = anotherList.begin(); it != anotherList.end(); it++){
if(*it != 9){
cout << *it << ", ";
}
else{
cout << *it;
}
}
cout << "]" << endl;

return 0;
}

问题出在"加载列表"代码块中,因为如果我将其注释掉,一切正常。

我是否正确保存了对象?我做错了什么?

提前谢谢。

问题在于reinterpret_cast<char *>(&numbers)逻辑有缺陷。为什么?

std::list使用指针管理其存储。它只是保存一个指向由某些对象组成的元素链的指针和一个指向下一个元素的指针。您不能简单地将其视为字节序列并期望它保持其功能。

相反,您需要做的是遍历元素并将它们逐个写入文件:

#include <fstream>
#include <iostream>
#include <list>
int main() {
std::fstream file{};
file.open("data.txt", std::ios::binary | std::ios::out);
std::list<int> ints{2, 4, 5, 6, 8, 1, 3, 5, 7, 9};
for (int i : ints) {
file.write(reinterpret_cast<char*>(&i), sizeof(i));
}
file.flush();
file.close();
file.open("data.txt", std::ios::binary | std::ios::in);
ints.clear();
std::cout << "Before reading the file, size of the list is: " << ints.size() << 'n';
for (int i; file.read(reinterpret_cast<char*>(&i), sizeof(i)); ints.push_back(i));
for (int i : ints) {
std::cout << i << ' ';
}
}

澄清第二个for循环:

for (int i; file.read(reinterpret_cast<char*>(&i), sizeof(i)); ints.push_back(i));
  • 我们声明一个变量i因为我们需要一个读取数据的地方。这个应该很清楚。我们不需要初始化i,因为循环的条件会解决这个问题(尽管无论如何这样做可能是一个很好的做法)。

  • 条件部分:file.read(reinterpret_cast<char*>(&i), sizeof(i))。乍一看这似乎很棘手,但事实并非如此!首先,我们有一个方法调用。我们调用std::basic_istream::read,指定两个参数 - 首先,读取变量的内存地址,其次,我们要读取的字节数。诀窍在于read方法不仅读取和保存数据 - 它还返回流,因此基本上在数据处理之后,我们只剩下条件file.但这不是bool,是吗?正确,它不是bool(也不是int),但流对象可以隐式转换为bool,这正是这里发生的事情!规则如下:如果流处于正确状态,则转换返回true。否则false返回。不正确的状态可能是由于读取失败造成的,例如,当您已经读取整个文件时,就会发生这种情况。本质上,此部分既从文件中读取,又检查读取过程是否成功执行。这既是阅读逻辑,也是条件!

  • 第三部分:ints.push_back(i)。请注意,仅当条件(从文件读取)成功执行时,才会执行此部分。它只是将读取int(i)添加到ints容器中。

总而言之,您可以通过以下方式读取for循环:

  • 创建一个变量i,它将逐个存储文件中的变量

  • 只要从文件中读取成功...

  • 。将读取值添加到容器

outputFile.write(reinterpret_cast<char *>(&numbers), sizeof(numbers));

您实际打印的是列表对象本身的二进制表示形式。不幸的是,它没有直接包含列表的数据,而是看起来像这样:

template <typename T>
class list
{
struct node
{
node* next;
node* previous;
T data;
};
node* m_head;
node* m_tail;
size_t m_size;
public:
// ...
};

没有直接链接到数据。更糟糕的是:使用std::list,数据可能会在您的内存中全部崩溃(与确保连续数据的std::vector相反)。

因此,您只能再次迭代列表(使用您之前已经选择的迭代器变体,或者更方便的是,使用基于范围 for 循环):

for(auto n : numbers)
{
outputFile.write(reinterpret_cast<char*>(&n), sizeof(n));
}

阅读是不同的;你事先不知道大小,是吗?嗯,有一些方法可以检索它(seekgtellg),但如果你想一次将所有数据读入连续内存(你可以在一个std::vectorreserve足够),但这是另一个问题。

对于列表方法:

int n;
while(inputFile.read(reinterpret_cast<char*>(&n), sizeof(n)))
{
anotherList.push_back(n);
}

最新更新