13.1使用CArchive类对文件进行读/写操作
我们可以利用CArchive类将对象数据永久地保存到磁盘文件上,再次重启后仍然可以读取对象数据。让对象数据持久性的过程就称为串行化。
CArchive对象可以认为就是一种二进制流。首先在创建CArchive对象之前必须先建一个CFile类或其派生类对象;当构造一个CArchive对象后,就可以将它与CFile类或者其派生类对象相关联。应该注意,一个文件(CFile对象)只能与一个活动的存档对象相关联。CArchive类重载了<<和>>运算符。
CArchive( CFile* pFile, UINT nMode, int nBufSize = 4096, void* lpBuf = NULL );
第一个参数指向文件对象的指针;
第二个参数指定对象是用来加载的还是用来保存的;
第三个对象是内部文件缓冲区的大小;
打开11章Graphic工程,添加一个文件操作的子菜单,在该子菜单下添加写入文件和读取文件的菜单项,ID为IDM_FILE_WRITE和IDM_FILE_READ。并添加各自的命令消息响应函数。
然后再OnFileWrite函数添加:
void CGraphicView::OnFileWrite()
{
// TODO: Add your command handler code here
//构造CFile文件对象
CFile file("1.txt",CFile::modeCreate|CFile::modeWrite);
//构造存档对象
CArchive ar(&file,CArchive::store);
int i=4;
char ch='a';
float f=1.3f;
CString str("vc++");
//保存数据
ar<<i<<ch<<f<<str;
}
运行程序,然后发现Graphic程序下多了一个1.txt文件
然后在OnFileRead函数中实现文件的读取操作:
void CGraphicView::OnFileRead()
{
// TODO: Add your command handler code here
//构造CFile文件对象
CFile file("1.txt",CFile::modeRead);
//构造存档对象
CArchive ar(&file,CArchive::load);
int i;
float f;
char ch;
CString str;
CString strResult;
//读取数据
ar>>i>>ch>>f>>str;
strResult.Format("%d,%c,%f,%s",i,ch,f,str);
MessageBox(strResult);
}
运行结果如图。
13.2MFC程序框架提供的文件新建功能
CGraphicDoc类有一个OnNewDocument函数,实际上,OnNewDocument函数是文件->新建命令处理的一部分,是由框架调用的一个虚函数。我们可以在CGraphicDoc类的OnNewDocument函数中修改文档的标题,这可以通过使用CDocument类中的成员函数:SetTitle。
BOOL CGraphicDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
// TODO: add reinitialization code here
// (SDI documents will reuse this document)
SetTitle("vc++");
return TRUE;
}
运行结果如图所示。
13.2.1IDR_MAINFRAME字符串资源
除了可以在OnNewDocument函数中修改文档标题之外,还可以通过IDR_MAINFRAME字符串资源来实现。如图。
该字符串是由\n字符分隔的7个子串,可以看到前两个\n之间没有任何内容,所以窗口标题显示无标题,可以在这两个\n之间来添加窗口标题。
一个资源ID可以标识多种资源,IDR_MAINFRAME不仅表示字符串资源,还表示菜单资源和图标资源。
对于我们看到的IDR_MAINFRAME字符串资源,可以利用文档模板类的成员函数:GetDocString来获取各个子串:
virtual BOOL GetDocString( CString& rString, enum DocStringIndex index ) const;
第一个参数是一个CString对象的引用;
第二个参数指定要查找字串的索引。
IDR_MAINFRAME第一个子串代表窗口的标题;第二个子串代表主窗口文档的标题,以及单击文件\保存菜单项时,默认的文档名;第三个子串指定的文档类型名出现在File\New对话框中;第四个子串与第五个子串连用,设置通配符过滤器,例如第四个子串"Text Files(*.txt)",第五个字串".txt"。
13.2.2OnNewDocument函数的调用过程
对于新建菜单项来说,应该有一个相应的菜单命令消息响应函数,但是OnNewDocument函数并不是一个消息响应函数,它是一个虚函数。因为OnNewDocument函数是虚函数,如果子类(CGraphicDoc)中存在重写这个函数,就会调用子类中的这个虚函数。
文件新建菜单命令->CWinApp::OnFileNew->CDocManager::OnFileNew->CSingleDocTemplate::OpenDocumentFile->CGraphicDoc::OnNewDocument
13.3文档串行化
13.3.1文档类的Serialize函数
我们可以看到文档类有一个Serialize函数:
void CGraphicDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: add storing code here
}
else
{
// TODO: add loading code here
}
}
该函数有一个参数,是CArchive类型。
单击文件打开和保存菜单项都会进入这个函数,这个函数就是文档类提供的用来保存和加载数据的函数。
下面将上述代码修改为:
void CGraphicDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: add storing code here
int i=4;
char ch='a';
float f=1.3f;
CString str("vc++");
ar<<i<<ch<<f<<str;
}
else
{
// TODO: add loading code here
int i;
float f;
char ch;
CString str;
CString strResult;
ar>>i>>ch>>f>>str;
strResult.Format("%d,%c,%f,%s",i,ch,f,str);
AfxMessageBox(strResult);
}
}
运行程序,选择另存为菜单项,使用默认的文件保存名Graphic.txt。
然后在Graphic目录下就可以看到该文件。在不关闭程序的情况下,单击打开菜单项,选择刚刚保存的文件,单击打开,但是没有显示读取结果的消息框。
原因:在没有关闭程序的情况下,它认为你的数据已经在视类窗口中,所以就没有调用文档类的Serialize函数。
13.3.2MFC框架对Serialize函数的调用过程
文件打开菜单命令->CWinApp::OnFileOpen->CDocManager::OnFileOpen->CWinApp::OpenDocumentFile->CDocManager::OpenDocumentFile->CSingleDocTemplate::OpenDocumentFile->CDocument::OnOpenDocument->CGraphicDoc::Serialize