CPP 将类写入/读取到二进制文件中

Posted

技术标签:

【中文标题】CPP 将类写入/读取到二进制文件中【英文标题】:CPP write/read class into binary file 【发布时间】:2021-10-18 08:53:43 【问题描述】:

我有 3 个文件,main.cpp(主程序)、user.h(用户类)和 userlist.h(用于存储用户的 LinkedList 数据结构)。

我想创建一个程序来将用户存储和读取到二进制文件中。我的问题是当我运行下面的 main.cpp(第一次运行)来保存和读取文件时,它会读取并显示正常数据。但是当我运行main.cpp(第二次运行)只读取刚刚保存的文件时,它不能正常工作。

我不知道为什么会发生这种情况。我的用户类属性类型是 char*、char* 和 bool。

// main.cpp (first run)

UserList userList;

User user1("admin", "root", 1);
User user2("aaaa", "dcasf", 1);
User user3("user3", "abcd", 0);
User user4("user4", "bbbb", 0);
userList.add(user1);
userList.add(user2);
userList.add(user3);
userList.add(user4);

userList.saveFile("user.txt");
userList.loadFile("user.txt");
userList.display();
// main.cpp (second run)

UserList userList;

userList.loadFile("user.txt");
userList.display();
// userlist.h

#ifndef USERLIST_H
#define USERLIST_H

#include "user.h"
using namespace std;


struct UserNode
    User user;
    UserNode* next = NULL;

    UserNode()

    UserNode(User user)
        this->user = user;
    

    UserNode(User user, UserNode* next)
        this->user = user;
        this->next = next;
    
;


class UserList

public:
    UserList()
        size = 0;
        isEmpty = true;

        head = NULL;
    ;

    UserList(const UserList& userList);

    ~UserList()
        clearAll();
    

    void add(User user)
        if(head == NULL)
            head = new UserNode();
            head->user = user;
        else
            UserNode* temp = head;

            while(temp->next != NULL)
                temp = temp->next;
            

            temp->next = new UserNode();
            temp->next->user = user;
        
    

    void clearAll()
        while(head != NULL)
            UserNode* temp = head;
            head = head->next;

            delete temp;
        

        head = NULL;
    

    User find(string username)
        UserNode* temp = head;

        while(temp != NULL)
            if(temp->user.getUsername() == username)
                return temp->user;
            

            temp = temp->next;
        

        User user;
        return user;
    

    bool contain(string username)
        UserNode* temp = head;

        while(temp != NULL)
            if(temp->user.getUsername() == username)
                return true;
            

            temp = temp->next;
        

        return false;
    

    void display()
        UserNode* temp = head;

        while(temp != NULL)
            cout << temp->user.getUsername() << "\t" << temp->user.getPassword() << "\t" << temp->user.getIsAdmin() << endl;
            temp = temp->next;
        
    

    void saveFile(string filename)
        ofstream output;
        output.open(filename, ios::out | ios::binary);

        UserNode* temp = head;

        while(temp != NULL)
            User user = temp->user;

            output.write((char*)&user, sizeof(user));

            temp = temp->next;
        

        output.close();
    

    void loadFile(string filename)
        clearAll();

        ifstream input;
        input.open(filename, ios::in | ios::binary);

        User user;
        while(input.read((char*)&user, sizeof(user)))  // read 1 user from file
            add(user);
        

        input.close();
    

private:
    int size;
    bool isEmpty;

    UserNode* head;
;
// user.h

#ifndef USER_H
#define USER_H

using namespace std;


class User

public:
    User()

    User(char* username, char* password, bool isAdmin)
        this->username = username;
        this->password = password;
        this->isAdmin = isAdmin;
    

    ~User()

    void setUsername(char* username)
        this->username = username;
    

    void setPassword(char* password)
        this->password = password;
    

    char* getUsername()
        return username;
    

    char* getPassword()
        return password;
    

    bool getIsAdmin()
        return isAdmin;
    

private:

    char* username;
    char* password;
    bool isAdmin;
;


#endif

【问题讨论】:

保存到文件的指针在读回时不太可能指向有效对象(除非您在写入后立即执行此操作,而这些对象仍然存在)。您需要阅读“序列化”。 @molbdnilo 但是我运行另一个具有相同上下文的程序,将 Box 类对象保存在文件中并在单独的运行时将其读回。它工作正常。 但是你的Box 类与显示的代码有什么关系?它有指针数据成员吗? @john 可能是因为您的 Box 类只包含数字或数组,而没有指针。 (但是你不能通过观察 C++ 程序的行为来判断它是否正确 - 未定义的行为可能会出于所有错误的原因做你所期望的。) 您的选项与已经提到的相同:1)不使用指针,或 2)实现正确的序列化。 【参考方案1】:

当你写入文件时,它会清除已经写入的数据。您应该使用附加模式写入文件

【讨论】:

我的问题不是那个。我的问题是,当我保存到文件中并在程序运行时读取文件时,它可以正常工作。但是当我只运行readFile()读取刚才保存的文件时,就不能正常工作了。【参考方案2】:

我认为你序列化一个带有 char 指针的结构的方式,只序列化 char 指针的地址。我们必须携带字符串的长度并用这个长度信息序列化每个字符串,以便我们可以准确地反序列化结构。

// userlist.h

#ifndef USERLIST_H
#define USERLIST_H

#include <stdio.h>
#include <iostream>
#include <fstream>
#include "user.h"
using namespace std;


struct UserNode
    User user;
    UserNode* next = NULL;

    UserNode()

    UserNode(User user)
        this->user = user;
    

    UserNode(User user, UserNode* next)
        this->user = user;
        this->next = next;
    
;


class UserList

public:
    UserList()
        size = 0;
        isEmpty = true;

        head = NULL;
    ;

    UserList(const UserList& userList);

    ~UserList()
        clearAll();
    

    void add(User user)
        if(head == NULL)
            head = new UserNode();
            head->user = user;
        else
            UserNode* temp = head;

            while(temp->next != NULL)
                temp = temp->next;
            

            temp->next = new UserNode();
            temp->next->user = user;
        
    

    void clearAll()
        while(head != NULL)
            UserNode* temp = head;
            head = head->next;

            delete temp;
        

        head = NULL;
    

    User find(string username)
        UserNode* temp = head;

        while(temp != NULL)
            if(temp->user.getUsername() == username)
                return temp->user;
            

            temp = temp->next;
        

        User user;
        return user;
    

    bool contain(string username)
        UserNode* temp = head;

        while(temp != NULL)
            if(temp->user.getUsername() == username)
                return true;
            

            temp = temp->next;
        

        return false;
    

    void display()
        UserNode* temp = head;

        while(temp != NULL)
            cout << temp->user.getUsername() << "\t" << temp->user.getPassword() << "\t" << temp->user.getIsAdmin() << endl;
            temp = temp->next;
        
    

    void saveFile(string filename)
        ofstream output;
        output.open(filename, ios::out | ios::binary);

        UserNode* temp = head;

        while(temp != NULL)
            User user = temp->user;
            output.write(reinterpret_cast<char*>(&user.sz_user), sizeof(int));
            output.write(user.getUsername(), user.sz_user * sizeof(char));
            output.write(reinterpret_cast<char*>(&user.sz_pass), sizeof(int));
            output.write(user.getPassword(), user.sz_pass * sizeof(char));
            output.write(reinterpret_cast<char*>(&user.admin_state) , sizeof(int));
            temp = temp->next;
        

        output.close();
    

    void loadFile(string filename)
        clearAll();

        ifstream input;
        input.open(filename, ios::in | ios::binary);

        User user;       
        while(input.read(reinterpret_cast<char*>(&user.sz_user), sizeof(int)))  // read 1 user from file
            char* usr = new char[user.sz_user];
            input.read(usr, user.sz_user * sizeof(char));
            user.setUsername(usr);

            input.read(reinterpret_cast<char*>(&user.sz_pass), sizeof(int));
            char* pass = new char[user.sz_pass];
            input.read(pass, user.sz_pass * sizeof(char));
            user.setPassword(pass);
            
            int adm ;
            input.read(reinterpret_cast<char*>(&adm),  sizeof(int));
            user.setAdmin(adm);

            add(user);
         
       
        input.close();
    

private:
    int size;
    bool isEmpty;

    UserNode* head;
;
#endif

以及 User 类中的小改动。 (您可以将上面的序列化读写携带到这个类中,以制作更多样式的代码。)

#ifndef USER_H
#define USER_H

using namespace std;
#include <string.h>
#include <iostream>

class User

public:
    User()

    User(char* username, char* password, bool isAdmin)
        this->username = username;
        this->password = password;       
        this->isAdmin = isAdmin;
        sz_user = strlen(username);
        sz_pass = strlen(password);
        admin_state = isAdmin ? 1: 0;       
    

    ~User()

    void setUsername(char* username)
        this->username = username;
    

    void setPassword(char* password)
        this->password = password;
    

    void setAdmin(int adm)  isAdmin = adm ? true : false ; 

    char* getUsername()
        return username;
    

    char* getPassword()
        return password;
    

    bool getIsAdmin()
        return isAdmin;
    

    int sz_user;
    int sz_pass;
    int admin_state;
    
private:

    char* username;  
    char* password;
    bool isAdmin;
    
;


#endif

【讨论】:

以上是关于CPP 将类写入/读取到二进制文件中的主要内容,如果未能解决你的问题,请参考以下文章

使用 C# 中的 ProtoBuf-Net 库将类数据保存到加密文件

如何读取和写入非固定长度的结构到二进制文件c++

c_cpp C ++写入二进制文件

c_cpp 将二进制数据写入c中的文件

c#二进制文件的写入和读取

在 Matlab 中读取和写入二进制文件