好友
阅读权限10
听众
最后登录1970-1-1
|
# 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>
​
#include <cstring> // 为了使用strlen和strcpy
#include <cstdio>
​
​
using namespace std;
​
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;
};
​
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);
​
}
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);
​
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;
}
​
​
​我写在这里大家品鉴 通过读取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);
}
​
void Human::UnSerializes(FILE* file) {
UnSerialize(file); // father
fread(&m_nId, sizeof(int), 1, file);
}
​这样继承了父类就可以 操作了但是读取时候并不知道出现什么那我问你那我问你 你打开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);
​
ClassType = CT_STUDENT;
fwrite(&ClassType, sizeof(ClassType), 1, file);
S2.Serialize(file) ;
ClassType = CT_HUMAN;
fwrite(&ClassType, sizeof(ClassType), 1, file);
human2.Serializes(file);
​
​
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);
}
​我把标识写进去不就得了
使用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;
​
​
​
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;
};
​
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);
}
​
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);
​
}
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);
​
ClassType = CT_STUDENT;
fwrite(&ClassType, sizeof(ClassType), 1, file);
S2.Serialize(file) ;
ClassType = CT_HUMAN;
fwrite(&ClassType, sizeof(ClassType), 1, file);
human2.Serializes(file);
​
​
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;
}
​
​
​
继承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;
}
}
​
private:
CString m_sName;
int m_nAge;
};
​
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;
}
​
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 成员函数。
实现原理就是这个样子 |
免费评分
-
查看全部评分
|