吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 360|回复: 1
收起左侧

[学习记录] MFC序列化反序列化过程 二分之一版本

[复制链接]
BGDK111 发表于 2024-12-11 21:59
#  I/O(序列化)

我要内存里的东西保存到文件保存文件,我要把文件里的东西读到内存 不得不品的两个操作

重新 品鉴stdio.h库首先定义点东西class Student {
public:
    Student() : m_nAge(0) {}
    Student(const string& Name, int Age) : m_szName(Name), m_nAge(Age) {}
​
private:
    string m_szName;
    int m_nAge;
};一个学生表然后创建int main()int main() {
    Student S1("shuaibi", 18);
    Student S2("dashuaibi", 19);
    Student S3("CSHshuaibi", 20);
​
    auto file = fopen(" Student.stu", "wb+");  // 自动  w
        if (file == 0) {
            return 0; // 错误处理
        }
  
        fwrite(&S1, sizeof(Student), 1, file);  //写入
        fwrite(&S1, sizeof(Student), 1, file);  //写入
        fwrite(&S1, sizeof(Student), 1, file);  //写入
​
        
        fclose(file);  // 关闭
    return 0;
}就可以实现 存入打开关闭
  • r 以只读方式打开文件,该文件必须存在。
  • r+ 以可读写方式打开文件,该文件必须存在。
  • rb+ 读写打开一个二进制文件,允许读数据。
  • rw+ 读写打开一个文本文件,允许读和写。
  • w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
  • w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
  • a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
  • a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留)
  • wb 只写打开或新建一个二进制文件;只允许写数据。
  • wb+ 读写打开或建立一个二进制文件,允许读和写。
  • ab+ 读写打开一个二进制文件,允许读或在文件末追加数据。
  • at+ 打开一个叫string的文件,a表示append,就是说写入处理的时候是接着原来文件已有内容写入,不是从头写入覆盖掉,t表示打开文件的类型是文本文件,+号表示对文件既可以读也可以写。
  • 上述的形态字符串都可以再加一个b字符,如rb、w+b或ab+等组合,加入b 字符用来告诉函数库以二进制模式打开文件。如果不加b,表示默认加了t,即rt,wt,其中t表示以文本模式打开文件。由fopen()所建立的新文件会具有S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH(0666)权限,此文件权限也会参考umask 值。有些C编译系统可能不完全提供所有这些功能,有的C版本不用"r+","w+","a+",而用"rw","wr","ar"等,读者注意所用系统的规定。

    输出流由于感觉我的代码不简洁所以我选择 优化一下 Un为反序列化// Iostream 控制台品鉴.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
    //
    #define _CRT_SECURE_NO_WARNINGS
    #include <iostream>
    #include <string>
    &#8203;
    #include <cstring> // 为了使用strlen和strcpy
    #include <cstdio>
    &#8203;
    &#8203;
    using namespace std;
    &#8203;
    class Student {
    public:
        Student() : m_nAge(0) {}
        Student(string Name, int Age) : m_szName(Name), m_nAge(Age) {}
        void Serialize(FILE* file);
        void UnSerialize(FILE* file);
        int nCount;
    private:
        string m_szName;
        int m_nAge;
    };
    &#8203;
    void Student::UnSerialize(FILE* file)
    {
        int nlen = 0;
       
        fread(&nlen, sizeof(int), 1, file);
        string* sString = new string[nlen + 1]{};//+{}不用memset
        fread(&m_szName, sizeof(nlen + 1), 1, file);
        fread((&m_nAge), sizeof(int), 1, file);
        fclose(file);
    &#8203;
    }
    void Student::Serialize(FILE* file) {
        int nLen = sizeof(m_szName);
        fwrite(&nLen, sizeof(int), 1, file);  //写入
        fwrite(&m_szName, sizeof(m_szName), 1, file);  //写入
        fwrite(&m_nAge, sizeof(int), 1, file);  //写入
       
    }
    int Writing() {
        Student S1("shuabi", 18);
        Student S2("bshuabi", 19);
        Student S3("dshuaibi", 20);
        auto file = fopen(" Student.stu", "wb+");  // 自动
        if (file == 0) {
            return 0; // 错误处理
        }
        int n = 3;
    fwrite(&n, sizeof(int), 1, file);
    S1.Serialize(file) ;
    S2.Serialize(file)  ;
    S3.Serialize(file);
    &#8203;
    fclose(file);
    }
    int main() {
      Writing();  // 序列化 读写
        auto file = fopen(" Student.stu", "rb+");  // 自动
        if (file == 0) {
            return 0; // 错误处理
        }
        int nCount = 0;
        fread(&nCount, sizeof(int), 1, file);
        Student* pArry = new Student[nCount];
        for (int i = 0; i < nCount; ++i) {
            pArry.UnSerialize(file);
        }
        fclose(file);
        // 反序列化 读取
        return 0;
    }
    &#8203;
    &#8203;
                   
    &#8203;我写在这里大家品鉴  通过读取n 计数 来for 循环 因为n在第一位


### 多类存储 现实中并不会出现这种 出现一整个的出现 我们可以选择继承这个招数 父类为Student class Human : public Student
{
    public:
        Human(){}
        Human(string Name, int Age,int nId):Student(Name,Age), m_nId(nId) {}
        void UnSerializes(FILE* file);
        void Serializes(FILE* file);
    private:
        int m_nId;
};
};
void Human::Serializes(FILE* file) {
    Serialize(file);  // father
    fread(&m_nId, sizeof(int), 1, file);
}
&#8203;
void Human::UnSerializes(FILE* file) {
    UnSerialize(file); // father
    fread(&m_nId, sizeof(int), 1, file);
}
&#8203;这样继承了父类就可以 操作了但是读取时候并不知道出现什么那我问你那我问你 你打开WinHex 你能看见 你用他读取的时候就分不出来了 我们使用枚举的这个招数 来快速识别文件 enum EnumHumStu{  // 枚举对象
    CT_HUMAN,
    CT_STUDENT
};
int Writing() {
  
    Student S1("shuabi", 18);
    Student S2("bshuabi", 19);
    Student S3("dshuaibi", 20);
   
    Human human1("cxk",27,1001);
    Human human2("LiTianSuo", 114, 1002);
    Human human3("Bochi", 20, 1003);
    auto file = fopen(" Student.stu", "wb+");  // 自动
    if (file == 0) {
        return 0; // 错误处理
        }
    int n = 6;
    fwrite(&n, sizeof(int), 1, file);
   
    EnumHumStu ClassType = CT_STUDENT;
    fwrite(&ClassType, sizeof(ClassType), 1, file);
    S1.Serialize(file) ;
   
    ClassType = CT_HUMAN;
    fwrite(&ClassType, sizeof(ClassType), 1, file);
    human1.Serializes(file);
&#8203;
   
    ClassType = CT_STUDENT;
    fwrite(&ClassType, sizeof(ClassType), 1, file);
    S2.Serialize(file)  ;
   
    ClassType = CT_HUMAN;
    fwrite(&ClassType, sizeof(ClassType), 1, file);
    human2.Serializes(file);
&#8203;
&#8203;
    ClassType = CT_STUDENT;
    fwrite(&ClassType, sizeof(ClassType), 1, file);
    S3.Serialize(file);
   
    ClassType = CT_HUMAN;
    fwrite(&ClassType, sizeof(ClassType), 1, file);
    human3.Serializes(file);
   
    fclose(file);
}
&#8203;我把标识写进去不就得了

使用MFC的 CArchive 进行序列化存储持久性是CArchive特点 但是我们咋么用呢 微软序列化文档标识了这一点
  • 从 CObject 派生类(或者从派生自 CObject 的某个类进行派生)。
  • 替代 Serialize 成员函数
  • 在类声明中的使用 DECLARE_SERIAL 宏
  • 定义没有参数的构造函数
  • 为你的类在实现文件中使用 IMPLEMENT_SERIAL 宏
我们只需要改造一下我们之前的 文档就行首先继承CObject类, 然后重写 Serialize函数,调用DECLARE_SERIAL宏,构造,最后使用这个宏我示范使用这个CArchive但是我得把 我这个改造的源码发出来// Iostream 控制台品鉴.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring> // 为了使用strlen和strcpy
#include <cstdio>
using namespace std;
&#8203;
&#8203;
&#8203;
class Student {
    public:
        Student() {}
        Student(string Name, int Age) : m_sName(Name), m_nAge(Age) {}
        void Serialize(FILE* file);  // 此类是 进行序列化也就是写入文件信息的
        void UnSerialize(FILE* file);// 这个是读取
        int nCount;
  
    private:
        string m_sName;
        int m_nAge;
};
&#8203;
class Human : public Student
{
    public:
        Human(){}
        Human(string Name, int Age,int nId):Student(Name,Age), m_nId(nId) {}
        void UnSerializes(FILE* file);
        void Serializes(FILE* file);
    private:
        int m_nId;
};
void Human::Serializes(FILE* file) {
    Serialize(file);  // father
    fread(&m_nId, sizeof(int), 1, file);
}
&#8203;
void Human::UnSerializes(FILE* file) {
    UnSerialize(file); // father
    fread(&m_nId, sizeof(int), 1, file);
}
enum EnumHumStu
{
    CT_HUMAN,
    CT_STUDENT
};
void Student::UnSerialize(FILE* file){
    int nlen = 0;
    fread(&nlen, sizeof(int), 1, file);
    string* sString = new string[nlen + 1]{};//+{}不用memset
    fread(&m_sName, sizeof(nlen + 1), 1, file);
    fread((&m_nAge), sizeof(int), 1, file);
&#8203;
}
void Student::Serialize(FILE* file) {
    int nLen = sizeof(m_sName);
    fwrite(&nLen, sizeof(int), 1, file);  //写入
    fwrite(&m_sName, sizeof(m_sName), 1, file);  //写入
    fwrite(&m_nAge, sizeof(int), 1, file);  //写入
}
int Writing() {
  
    Student S1("shuabi", 18);
    Student S2("bshuabi", 19);
    Student S3("dshuaibi", 20);
   
    Human human1("cxk",27,1001);
    Human human2("LiTianSuo", 114, 1002);
    Human human3("Bochi", 20, 1003);
    auto file = fopen(" Student.stu", "wb+");  // 自动
    if (file == 0) {
        return 0; // 错误处理
        }
    int n = 6;
    fwrite(&n, sizeof(int), 1, file);
   
    EnumHumStu ClassType = CT_STUDENT;
    fwrite(&ClassType, sizeof(ClassType), 1, file);
    S1.Serialize(file) ;
   
    ClassType = CT_HUMAN;
    fwrite(&ClassType, sizeof(ClassType), 1, file);
    human1.Serializes(file);
&#8203;
   
    ClassType = CT_STUDENT;
    fwrite(&ClassType, sizeof(ClassType), 1, file);
    S2.Serialize(file)  ;
   
    ClassType = CT_HUMAN;
    fwrite(&ClassType, sizeof(ClassType), 1, file);
    human2.Serializes(file);
&#8203;
&#8203;
    ClassType = CT_STUDENT;
    fwrite(&ClassType, sizeof(ClassType), 1, file);
    S3.Serialize(file);
   
    ClassType = CT_HUMAN;
    fwrite(&ClassType, sizeof(ClassType), 1, file);
    human3.Serializes(file);
   
    fclose(file);
}
int main(){
  Writing();  // 序列化 读写
    auto file = fopen(" Student.stu", "rb+");  // 自动
    if (file == 0){
        return 0; // 错误处理
    }
   
    int nCount = 0;
    fread(&nCount, sizeof(int), 1, file);
    Student* pArry = new Student[nCount];
     for (int i = 0; i < nCount; ++i) {
        pArry.UnSerialize(file);
    }
    fclose(file);
    // 反序列化 读取
    return 0;
}
&#8203;
&#8203;
&#8203;

继承CObject类MFC序列化反序列化实现步骤
首先需要 调用MFC类的窗口程序 然后呢 就需要改造一下 咱们的Class 根据 文档要求           class Student : public CObject 的public CObject是必要的
  • 其次要声明 一下 序列化函数
  • 然后公类声明宏
  • 构造空类
我演示一下 (部分)class Student : public CObject
{
public:
    DECLARE_SERIAL(Student)
public:
   
     Student() {}  // 要求
     Student(CString Name, int Age) : m_sName(Name), m_nAge(Age) {}
     void Serialize(FILE* file);  // 此类是 进行序列化也就是写入文件信息的
     void UnSerialize(FILE* file);// 这个是读取
     int nCount;
     void Serialize(CArchive& archive) {
        CObject::Serialize(archive);//调父类
        if (archive.IsStoring()) {  // 正在存储 序列化存储
            archive << m_sName << m_nAge;
        }
        else{
            archive >> m_sName >> m_nAge;
        }
     }
&#8203;
private:
    CString m_sName;
    int m_nAge;
};
&#8203;
class Human : public Student {  
public:
    DECLARE_SERIAL(Human)
public:
    Human() {}
    Human(CString Name, int Age, int nId) :Student(Name, Age), m_nId(nId) {}
    void UnSerializes(FILE* file);
    void Serialize(CArchive& archive) {
        CObject::Serialize(archive);//调父类
        if (archive.IsStoring()) {  // 正在存储 序列化存储
            archive << m_nId;
         }
&#8203;
        else{
            archive >> m_nId;
        }
        
    }
private:
    int m_nId;
};最后一步 实现函数里声明宏IMPLEMENT_SERIAL 宏用于定义从 CObject 派生可序列化类时所需的各种功能。 您为您的类在实现文件 (.cpp) 中使用此宏。 此宏的前两个参数是类的名称和其直接基类的名称。该宏的第三个参数是一个架构数字。 架构数字本质上是类的对象的版本号。 请对架构数字使用大于或等于 0 的整数值。 (不要将此架构数字与数据库术语混淆。)MFC 序列化代码在将对象读入内存时检查架构数字。 如果磁盘上的对象的架构数字与内存中的架构数字不匹配,库将引发 CArchiveException,这会阻止您的程序读取不正确的版本的对象。如果希望 Serialize 成员函数能够读取多个版本(即,用不同版本的应用程序编写的文件),可使用值 VERSIONABLE_SCHEMA 作为 IMPLEMENT_SERIAL 宏的自变量。 有关用法信息和示例,请参阅类 GetObjectSchema 的 CArchive 成员函数。以下示例演示如何为派生自 CObject 的类 CPerson 使用 IMPLEMENT_SERIAL:IMPLEMENT_SERIAL(Student, CObject, 1)这是直接实现 因为这个是 在实现的文件里声明就行 我的程序是实现声明是一个文件里的 所以 我直接在类外声明IMPLEMENT_SERIAL(Student, CObject, 1)//实现声明 这里声明和实现是一个文件 数字版本号
IMPLEMENT_SERIAL(Human, Student, 1)获得可序列化的类后,可序列化该类的对象,如序列化:序列化对象一文所述。不同版本声明如果希望 Serialize 成员函数能够读取多个版本(即,用不同版本的应用程序编写的文件),可使用值 VERSIONABLE_SCHEMA 作为 IMPLEMENT_SERIAL 宏的自变量。 有关用法信息和示例,请参阅类 GetObjectSchema 的 CArchive 成员函数。

实现原理就是这个样子

免费评分

参与人数 2吾爱币 +2 热心值 +1 收起 理由
BrutusScipio + 1 用心讨论,共获提升!是浏览器问题吗,为什么排版控制字符全出来了
G孟纸 + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

lxxfhtd 发表于 2024-12-11 23:36
谢谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-26 21:35

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表