(二)CArchive类操作
CArchive类实现数据的缓冲区读写,同时定义了类对象的存储与读取方案。 在文档视图编程,结合CList和CArray等可很方便的实现数据读写。
(1)基本数据读写----显示创建CArchive对象
A. 使用CArhive类之前,必须要有一个CFile文件对象,同时必须保证CArchive的操作必须是在CFile处于打开状态,且操作期间文件状态不会发生变化。
B. 然后定义CArchive对象,将其与CFile对象关联,并指定其模式是用于缓冲区读还是写:
CArchive ar (pFile, //目标文件,CFile对象指针
nMode, //操作方式, 读/写
nBufSize, //指定缓冲区大小,默认为4096
lpBuf //缓冲指针,默认为NULL,即从本地堆中取出一块内存空间,操作结束后自动释放
);
C.对于基本数据类型,可直接使用operator >>和operator <<进行数据读出和写入。这些数据类型BYTE、WORD、LONG、DWORD、float、double、int、short、char、unsigned u等。 对于CString对象也可使用。
对于自定义的结构体数据类型,我们自己可以重载这两个运算符,一个示例如下。
01.typedef struct test_st
02.{
03. float valueX;
04. float valueY;
05.
06. //重载<<和>>运算符
07. friend CArchive& AFXAPI operator <<(CArchive& ar, const test_st& info)
08. {
09. //保存
10. ar<<info.valueX<<info.valueY;
11. return ar;
12. }
13. friend CArchive& AFXAPI operator >>(CArchive& ar, test_st& info)
14. {
15. //读取
16. ar>>info.valueX>>info.valueY;
17. return ar;
18. }
19.}TEST;
D. 使用Read和Write可实现指定数据长度的数据写入/读取,这里的数据长度指的是字节数。
使用WriteString可写入字符串,ReadString可读出一行字符。注意这里WriteString并不是写入一行字符串,WriteString写入的字符串时不会写入'\0',也不会自动写入'\n'。下面是来自MSDN的一个例子,相信能说明问题。
01.CFile myFile("myfile", CFile::modeCreate | CFile::modeReadWrite);
02.CString str1="String1", str2="String2", str;
03.
04.// Create a storing archive.
05.CArchive arStore(&myFile, CArchive::store);
06.
07.// Write str1 and str2 to the archive
08.arStore.WriteString( str1 );
09.arStore.WriteString( "\n" );
10.arStore.WriteString( str2 );
11.arStore.WriteString( "\n" );
12.
13.// Close the storing archive
14.arStore.Close();
15.
16.// Create a loading archive.
17.myFile.SeekToBegin();
18.CArchive arLoad(&myFile, CArchive::load);
19.
20.// Verify the two strings are in the archive.
21.arLoad.ReadString( str );
22.ASSERT( str == str1 );
23.arLoad.ReadString( str );
24.ASSERT( str == str2 );
E. IsLoading 和 IsStoring函数常用来判断是读取还是写入。
Close函数用于切断与CFile对象的关联,在这之前会自动调用Flush将缓冲区数据写入存储媒质中。
F. 在程序中,如果没有调用函数Flush(),那么真正将数据写入到物理磁盘是在调用函数Close()关闭时。因此,一些重要的数据需要使用Flush()函数立即写入文件,以防丢失。
(2)类对象的读写
A. 利用CArchive保存/加载一个类对象,则此类必须支持串行化。
B. 自定义串行化类的五个步骤:
①继承CObject类;
②重载CObject类的Serialize成员函数;
③在类的.h文件中,进行串行化的声明:DECLARE_SERIAL(类名);
④定义一个无参数的构造函数;
⑤在类的.cpp文件中,进行声明:IMPLEMENT_SERIAL(类名, CObject, 版本号)
C. 在自定义的类的Serialize成员函数下,使用上述(1)中的方法,保存/加载基本数据类型。如下。在需要文件操作的地方,直接调用该函数即可。
01.void CXXXX::Serialize(CArchive &ar)
02.{
03. if (ar.IsStoring())
04. { //保存
05. //Add code....
06. }
07. else
08. { //读取
09.
10. //Add code...
11. }
12.}
D. 利用此方法,可实现数据的分布式保存。通常在文档视图编程时,我们在文档类下定义数据对象,然后在其Serialize函数下实现数据的保存/加载即可。
通常我们会定义一个容器,用来存放数据。例如,在我的项目中,我使用了
CTypedPtrList<CObList, CObject*> m_DataList;
这是一个链表,里面元素为CObject类对象的指针,当我们自定义了支持串行化的类后,就可以把数据加入到这个链表中,很方便的实现数据管理和存储。