如何将链表写入二进制文件并读取,避免程序崩溃丢失节点数据?
嘿,我来帮你搞定链表节点持久化到二进制文件的问题!先从你现有代码里的几个小问题说起,再一步步实现正确的保存和读取逻辑。
先修正现有代码的小错误
你的代码里有几个编译不过的小问题,得先搞定:
Node类里的doble是拼写错误,应该是double;addNode方法里用了newnode->data,但Node类里并没有data成员,应该对应你定义的code(或者price/name,看你实际要存的数值);- 析构函数是空的,记得要遍历链表释放节点,不然会内存泄漏。
为什么你设想的saveOn方法不可行?
你当前的saveOn用的是文本输出(os << ...),而且只存了单个字段,既不是二进制格式,也没有保存节点的所有数据(比如price、name)。更关键的是:绝对不能保存next指针——程序重启后,内存地址是重新分配的,旧指针完全无效,我们只需要保存节点的业务数据,读取时重新构建链表结构。
实现二进制保存(saveOn)
要写入二进制文件,我们需要用ostream::write()方法,把每个节点的非指针数据序列化到文件。对于std::string,因为它是动态长度的,不能直接写入整个对象,得先写入字符串的长度,再写入字符内容。
修改后的核心代码如下:
#include <fstream> #include <string> #include <iostream> // 修正Node类的拼写错误 class Node { public: int code; double price; std::string name; Node* next; }; class LinkedList{ public: LinkedList() : head(nullptr) {} ~LinkedList() { // 析构函数添加节点释放逻辑 Node* temp = head; while (temp != nullptr) { Node* next = temp->next; delete temp; temp = next; } } void addNode(int code, double price, const std::string& name); void reverseList(); void display(); bool saveOn(const std::string& filename); bool loadFrom(const std::string& filename); private: Node* head; }; // 修正addNode方法,支持添加完整节点数据 void LinkedList::addNode(int code, double price, const std::string& name) { Node* newnode = new Node(); newnode->code = code; newnode->price = price; newnode->name = name; newnode->next = nullptr; if (head == nullptr) { head = newnode; } else { Node* temp = head; while (temp->next != nullptr) { temp = temp->next; } temp->next = newnode; } } // 实现二进制保存方法 bool LinkedList::saveOn(const std::string& filename) { std::ofstream os(filename, std::ios::binary); if (!os.is_open()) { std::cerr << "Failed to open file for writing!" << std::endl; return false; } Node* temp = head; while (temp != nullptr) { // 写入int类型的code os.write(reinterpret_cast<const char*>(&temp->code), sizeof(temp->code)); // 写入double类型的price os.write(reinterpret_cast<const char*>(&temp->price), sizeof(temp->price)); // 处理string:先写入长度,再写入字符 size_t nameLen = temp->name.size(); os.write(reinterpret_cast<const char*>(&nameLen), sizeof(nameLen)); os.write(temp->name.c_str(), nameLen); temp = temp->next; } os.close(); return true; }
实现从二进制文件读取(loadFrom)
读取的时候,我们需要按保存的顺序反向操作:先读code、price,再读字符串长度和字符,然后创建节点添加到链表。注意读取前要清空现有链表的节点,避免内存泄漏。
bool LinkedList::loadFrom(const std::string& filename) { // 先清空现有链表 Node* temp = head; while (temp != nullptr) { Node* next = temp->next; delete temp; temp = next; } head = nullptr; std::ifstream is(filename, std::ios::binary); if (!is.is_open()) { std::cerr << "Failed to open file for reading!" << std::endl; return false; } while (true) { Node* newnode = new Node(); // 读取code,读到文件末尾则退出 if (!is.read(reinterpret_cast<char*>(&newnode->code), sizeof(newnode->code))) { delete newnode; break; } // 读取price is.read(reinterpret_cast<char*>(&newnode->price), sizeof(newnode->price)); // 读取string长度和内容 size_t nameLen; is.read(reinterpret_cast<char*>(&nameLen), sizeof(nameLen)); char* nameBuf = new char[nameLen + 1]; is.read(nameBuf, nameLen); nameBuf[nameLen] = '\0'; // 确保字符串以null结尾 newnode->name = nameBuf; delete[] nameBuf; newnode->next = nullptr; // 添加到链表末尾 if (head == nullptr) { head = newnode; } else { Node* tail = head; while (tail->next != nullptr) { tail = tail->next; } tail->next = newnode; } } is.close(); return true; }
使用示例
int main() { LinkedList list; list.addNode(1, 9.99, "Apple"); list.addNode(2, 19.99, "Banana"); list.display(); // 保存到文件 list.saveOn("linked_list_data.bin"); // 创建新链表,读取文件内容 LinkedList newList; newList.loadFrom("linked_list_data.bin"); newList.display(); // 输出应该和原链表完全一致 return 0; }
注意事项
- 打开文件时一定要加上
std::ios::binary标志,避免系统自动转换换行符等文本格式; - 序列化和反序列化的顺序必须严格一致(先存的先读);
- 如果你的
Node类以后添加了新成员,要同步修改保存和读取逻辑,不然会导致数据错乱; - 可以考虑添加一个"节点数量"的头信息,先写入链表的节点总数,读取时按数量读取,这样比读到文件末尾更可靠。
内容的提问来源于stack exchange,提问作者josilva00
相关产品推荐
相关产品推荐

