首页 > 代码库 > C++MFC编程笔记day07 MFC的文件操作、序列化和保存

C++MFC编程笔记day07 MFC的文件操作、序列化和保存

一 MFC的文件操作
   1 相关类
     CFile类-封装了文件句柄以及操作文件的API函数。
     CFileFind类-提供文件查找功能。
   2 CFile类的使用
     2.1 打开或者新建文件
         CFile::Open
     2.2 文件读写
         注意:1 文件指针位置 2 异常处理
         CFile::Write
         CFile::Read
         CFile::SeekToBegin
     2.3 关闭文件
         CFile::Close
     2.4 设置/获取文件属性
         CFile::SetStatus/GetStatus
   3 CFileFind类的使用
     3.1 开始查找,返回值代表是否有文件存在
         CFileFind::FindFile
     3.2 获取找到的第一个文件信息,返回下一个文件是否存在
         CFileFind::FindNextFile
     3.3 获取文件信息和判断文件信息
         CFileFind::GetXXX/IsXXX
     3.4 结束查找
         CFileFind::Close
     例子:1 使用该类查找C:根目录下所有的文件和文件夹
           注意:磁盘根目录中不存在.目录,只有在下一层目录中才
           存在.目录。
           2 使用该类查找C:下所有的文件和文件夹
             2.1 递归,判断如果是目录,调用函数本身
                 strPath=find.GetFilePath()
             2.2 排除.目录
                 IsDots


二 序列化
   1 概念
     将数据以二进制流的方式依次写入到文件和从文件中读取数据的过程。
   2 相关类
     CFile类
     CArchive类-归档类,提供了具体的数据读写的功能。
     引入该类的好处,一,可以设置读写时的缓冲区;二,支持多种数据
     类型的读写。
   3 读写操作
     3.1 打开或者新建文件
         CFile::Open
     3.2 文件读写
         3.2.1 定义CArchive类的对象
         3.2.2 读写操作符函数
               << 写操作
               >> 读操作
         3.2.3 关闭
     3.3 关闭文件CArchive类的对象
         CFile::Close

三 对象的序列化(第6个机制)
   引入对象的序列化,解决用户自定义类的对象的读写
   1 概念
   序列化对象(object store)
   将对象的类的信息和对象的成员变量依次写入到文件的过程。
   反序列化对象(object load)
   从文件首先读取类的信息,创建对象,然后读取成员变量的值初始化
   对象的过程。
   结论:对象的序列化以运行时类信息和动态创建为基础的
   2 使用
   2.1 定义支持序列化的类 **
       2.1.1 必须是CObject类的子类
       2.1.2 添加序列化的声明宏和实现宏
       2.1.3 重写虚函数 CObject::Serialize,在函数中,完成
             类中的成员变量的序列化。
   2.2 使用步骤与一般的数据类似
       具体读写操作时,参数是对象的地址
   3 原理
     3.1 成员
     _init_CStudent-结构体类型的成员对象,当定义该对象时,就会将
     当前类的运行时类信息保存到应用程序中。
     struct AFX_CLASSINIT
     {
    AFX_CLASSINIT(CRuntimeClass* pNewClass)
       {
           AfxClassInit(pNewClass);
           {
             //获取应用程序类的模块状态信息
             AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
         AfxLockGlobals(CRIT_RUNTIMECLASSLIST);
             //保存当前类的运行时类信息(classCstudent的地址)
         pModuleState->m_classList.AddHead(pNewClass);
         AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);
           }
       };
     };
     3.2 序列化对象的过程
     ar.WriteObject(pOb);
     {
        //1 获取当前类的运行时类信息
        CRuntimeClass* pClassRef = pOb->GetRuntimeClass();
        //2 将类的信息写入到文件
        WriteClass(pClassRef);
        {
           pClassRef->Store(*this);
           {
              //依次将类的版本,类名称长度以及类的名称写入文件
              WORD nLen = (WORD)lstrlenA(m_lpszClassName);
          ar << (WORD)m_wSchema << nLen;
          ar.Write(m_lpszClassName, nLen*sizeof(char));
           }
        }
        //3 调用Serilize函数存储对象的成员变量
        ((CObject*)pOb)->Serialize(*this);
        {
            //首先调用父类的序列化函数
       CObject::Serialize( ar );
       if (ar.IsStoring())//存储操作
       {
               ar<<m_strName<<m_nAge;
       }
       else//加载操作
       {
               ar>>m_strName>>m_nAge;
       }
        }

     }
   3.3 反序列化对象的过程
   ar.ReadObject(RUNTIME_CLASS(CStudent));
   {
      //1 读取当前类的运行时类信息,得到运行时类信息变量的地址
      ReadClass(pClassRefRequested, &nSchema, &obTag);
      {
         //  从文件中读取类的信息,比较并得到运行时类信息
         pClassRef = CRuntimeClass::Load(...)
         {
           //读取类的名称
           ar.Read(szClassName, nLen*sizeof(char));
           //遍历链表,根据类的名称,查找运行时类信息
           for (pClass = pModuleState->m_classList; pClass != NULL;
        pClass = pClass->m_pNextClass)
           {
              //具体的比较类的名称
              if (lstrcmpA(szClassName, pClass->m_lpszClassName) == 0)
          {
         return pClass;
          }
           }
         }
      }
      // 2 根据得到的运行时类信息,动态创建对象
      pOb = pClassRef->CreateObject();
      //3 从文件中读取成员变量初始化新建的对象
      pOb->Serialize(*this);
      {
          //首先调用父类的序列化函数
       CObject::Serialize( ar );
       if (ar.IsStoring())//存储操作
       {
               ar<<m_strName<<m_nAge;
       }
       else//加载操作
       {
               ar>>m_strName>>m_nAge;
       }
      }

   }



程序示例:

新建win32控制台程序 ,添加afxwin.h头文件,设置包含MFC动态库


主程序代码如下:

// MFCfile.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <AFXWIN.H>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//打印文件状态信息
void show_status(CFileStatus& status)
{
	printf("文件名:%s\n",status.m_szFullName);
	printf("大小:%g\n",status.m_size);
}
//打印空格 ,为了清楚地显示目录结构
void printspace(int n)
{
	if(n>0)
	{
		for(int i=0;i<n;i++) printf(" ");
	}
}
//文件读取和写入
void CFileTest()
{
	CFile file;//定义CFile对象
	//打开
	CString filename="MFCfile.txt";
	BOOL bresult=file.Open(filename,
		CFile::modeCreate|CFile::modeReadWrite);
	if(!bresult) return;//打开失败,直接返回
	try
	{
		CString str="Hello my cfile!";
		file.Write(str,str.GetLength());
		printf("写入成功\n");
		char sztxt[100]={0};
		file.SeekToBegin();//将文件指针移动到文件开头
		file.Read(sztxt,100);
		printf("读取成功,字符串:%s\n",sztxt);
		file.Close();

		CFileStatus status;
		//如果其他程序正在使用该文件,有可能出CFileException异常
		CFile::GetStatus(filename,status);//获取状态信息
		show_status(status);
		CTimeSpan span(7,0,0,0);//时间间隔,7天
		status.m_ctime-=span;//修改创建时间提前7天
		CFile::SetStatus(filename,status);//设置状态信息

	}
	catch (CMemoryException* e)
	{
		printf("内存出错\n");
	}
	catch (CFileException* e)
	{
		printf("文件操作出错\n");
	}
	catch (CException* e)
	{
		printf("其他错误\n");
	}
}
//文件查找
void CFileFindTest(CString strPath,int space=0)
{
	CFileFind cf;
	BOOL result=cf.FindFile(strPath+"\\*.*");//目录后要加通配符
	
	while(result)
	{
		result=cf.FindNextFile();
		CString filename=cf.GetFileName();
		printspace(space);
		if(cf.IsDirectory()) 
		{
			if(!cf.IsDots())
			{
				CString filepath=cf.GetFilePath();
				//printf("   subdir:%s\n",filepath);
				CFileFindTest(filepath,space+1);
			}
			printf("目录名:%s\n",filename);
		}
		else printf("文件名:%s\n",filename);
		
	}
	cf.Close();

}
//序列化(二进制),写入数据
void CArchiveStoreTest()
{
	CFile file;//定义CFile对象
	//打开
	CString filename="MFCfile1.txt";
	BOOL bresult=file.Open(filename,
		CFile::modeCreate|CFile::modeWrite);
	if(!bresult) return;//打开失败,直接返回
	CArchive ar(&file,CArchive::store);
	ar<<100<<12.46<<"hello archive!";//写入
	ar.Close();
	file.Close();
}
//序列化(二进制),读取数据
void CArchiveloadTest()
{
	CFile file;//定义CFile对象
	//打开
	CString filename="MFCfile1.txt";
	BOOL bresult=file.Open(filename,
		CFile::modeNoTruncate|CFile::modeRead);
	if(!bresult) return;//打开失败,直接返回
	CArchive ar(&file,CArchive::load);
	int i;
	double d;
	CString str;
	ar>>i>>d>>str;//读取
	ar.Close();
	file.Close();
	printf("%d,%f,%s\n",i,d,str);
}
//1 定义支持序列化的类
class CStudent:public CObject
{
public:
	virtual void Serialize( CArchive& ar );//重写虚函数
	CStudent(){}
	CStudent(CString strName,UINT nAge)
	{
		m_strName=strName;
		m_nAge=nAge;
	}
	void Show()
	{
		printf("姓名:%s\n",m_strName);
		printf("年龄:%d\n",m_nAge);
	}
	
public:
	CString m_strName;
	UINT m_nAge;
	//序列化的宏
	DECLARE_SERIAL(CStudent)
	/*  //宏内容
	_DECLARE_DYNCREATE(CStudent) 
		AFX_API friend CArchive& AFXAPI 
		operator>>(CArchive& ar, CStudent* &pOb);
	*/
};
IMPLEMENT_SERIAL(CStudent,CObject,1)
/* //宏内容
CObject* PASCAL CStudent::CreateObject() 
{ 
	return new CStudent;
} 
_IMPLEMENT_RUNTIMECLASS(CStudent, CObject, 1, 
						CStudent::CreateObject)
AFX_CLASSINIT _init_CStudent(RUNTIME_CLASS(CStudent));

CArchive& AFXAPI operator>>(CArchive& ar, CStudent* &pOb)
{ 
	pOb =  (CStudent*) ar.ReadObject(RUNTIME_CLASS(CStudent)); 
	return ar; 
} 
*/
void CStudent::Serialize( CArchive& ar )
{
	//首先调用父类的序列化函数
	CObject::Serialize( ar );
	if (ar.IsStoring())//存储操作
	{
		ar<<m_strName<<m_nAge;
	}
	else//加载操作
	{
		ar>>m_strName>>m_nAge;
	}
}
//存入
void ObjectStore(CStudent & stu)
{
	CFile file;
	file.Open("C:\\stu.dat",
		CFile::modeCreate|CFile::modeWrite);
	CArchive ar(&file,CArchive::store);
	ar<<&stu;
	ar.Close();
	file.Close();
}
//读出
void ObjectLoad()
{
	CFile file;
	file.Open("C:\\stu.dat",CFile::modeRead);
	CArchive ar(&file,CArchive::load);
	CStudent *pStu=NULL;
	ar>>pStu;
	ar.Close();
	file.Close();
	if (pStu)
	{
		pStu->Show();
	}
}
int main(int argc, char* argv[])
{
    //CFileTest();
	//CFileFindTest("../../");
	CArchiveStoreTest();
	CArchiveloadTest();
	CStudent stu("student1",23);
    ObjectStore(stu);
	ObjectLoad();
	getchar();
	return 0;
}