1、VC+文件操作实验9 文件操作实验目的1、 掌握文档串行化的原理2、 掌握用CFile类实现文件的读写操作3、 掌握文件的输入/输出流实验内容主要讲述文件操作的有关知识,内容包括文档串行化、CFile类、内存稳健类和文件的输入/输出,重点讲述了文件操作实现的两种方法串行化和CFile类。文件管理是操作系统的一个重要组成部分,而文件操作就是通过用户编程实现文件管理的一种手段。基本概念:串行化、CArchive类、CFile类、文件的读写操作、文件的管理、文件异常、文件对话框、CMemFile类、CSharedFile类、文件输入/输等,见PPT文档。在MFC AppWizard生成MFC App
2、Wizardexe程序框架时,如果在MFC AppWizardStep1对话框中选择了Document/View architecture support,将会在自动生成的程序中支持CDocument派生类的Serialize成员函数的操作。在MFC AppWizardStep4 of 6对话框中有Advanced按钮,弹出Advanced Options对话框,如下图:输入项信息说明File extension文件扩展名File type ID注册表中等级的文档类型项目的标签文字Main frame caption应用程序的标题Doc type name文档类型名,限制6个字母,多文档程序用
3、这个名字加上数字序号作为新建文件的文件名Filter name文档描述过滤符File new name(short name)多个文档模版时新建对话框中显示的该文档文字信息文件,限制15个字符File type name(long name)注册表记录的文件类型名,限制40个字母例 创建统计年度产值程序,实现文件操作。详细步骤如下:1、 创建一个AppWizardexe类型的MDI工程,名称为ex101。在MFC AppWizardStep 1对话框中选择Document/View architecture support如上第一图。为了实现视图类的界面,使用了ActiveX Controls
4、,所以要在MFC AppWizardStep 3 of 6对话框中选择ActiveX Controls,以使MFC支持ActiveX;否则,程序将不能正常运行。在MFC AppWizardStep 4 of 6对话框中,单击Advanced按钮,设置文档类型,如下图在MFC AppWizardStep 6 of 6对话框中,将CEx101View视图类的基类选择为CFormView,单击OK完成。2、 设计CEx101View类的界面。打开ResourceView双击Dialog中的IDD_EC101_FORM项,可以编辑CEx101View使用的对话框资源。按照下图的排列,增加下表中的控件。
5、控件含义ID类型属性年份IDC_YEAREdit Box选中StylesNumber改变年份SpinIDC_SPIN_YEARSpin默认月份IDC_MONTHCombo BoxData输入“一月”到“十二月”,StylesType去掉Sort当月产值IDC_MON_PROEdit Box选中StylesNumber当月备注IDC_MON_MEMEdit Box默认比上月增长率IDC_RISERATEStatic Text删除Caption全年产值IDC_YEAE_PROForm Label删除Caption,黑体14在输入一月到十二月时,使用Ctrl+Enter换行。窗口上的其他文字信息是S
6、tatic Text控件,设定好位置后写上文字即可。添加全年产值控件的方法是:在编辑对话框的窗口上,按鼠标右键选择Insert ActiveX Controls,在弹出的对话框上添加Microsoft Forms 2.0 Label控件,选择其属性,可以设置较为美观的字体、颜色。3、给CEx101Doc类添加成员变量。给CEx101Doc类添加记录各月产值、备注、年份等的变量,可以使用鼠标右键弹出菜单中的Add Member Variable项,添加部分变量并定位到源代码中变量定义处,继续添加下列代码:public: struct int nProduct; /月份产量 char sNote5
7、0; /备注 m_Month12; /记录各月信息 int m_nTotalProduct;/年总产量 int m_nYear; /年份在CEx101Doc类的构造函数中对这些变量进行初始化:CEx101Doc:CEx101Doc() m_nYear=2005; m_nTotalProduct=0; for(int i=0;im_nYear; /获取文档中的年份 m_ctrlMonthList.SetCurSel(0);/选择一月 m_nCurrentMonth=0; /当前月是一月 m_nMonPro=pDoc-m_Month0.nProduct;/一月产值 m_strMonMem=pDoc
8、-m_Month0.sNote;/一月备注 m_strRiseRate=第一月无此值; CString str; str.Format(%d,pDoc-m_nTotalProduct); m_YearPro.SetCaption(str); /调用CLableControl设置标题函数显示年总产值 UpdateData(false);C、添加当月备注函数OnChangeMonMem()。使用类向导在CEx101View类中增加成员函数,用于响应输入当月备注控件IDC_MON_MEM的EN_CHANGE消息,其代码如下:void CEx101View:OnChangeMonMem() CEx10
9、1Doc* pDoc=GetDocument(); UpdateData(); /获取最新输入数据 /和文档中的数据比较,看是否有变化 if(strcmp(pDoc-m_Monthm_nCurrentMonth.sNote,m_strMonMem) /记录新数据 strcpy(pDoc-m_Monthm_nCurrentMonth.sNote,m_strMonMem); pDoc-SetModifiedFlag(); /设置文档改变标志 D、添加当月产值OnChangeMonPro()函数。使用类向导在CEx101View类中增加成员函数,用于响应输入当月产值控件IDC_MON_PRO的EN_
10、CHANGE消息,其代码如下:void CEx101View:OnChangeMonPro() CEx101Doc* pDoc=GetDocument(); UpdateData(); /获取输入的月份值 if(pDoc-m_Monthm_nCurrentMonth.nProduct!=m_nMonPro) /比较判断数据是否改变,并改变总产值 pDoc-m_nTotalProduct+=m_nMonPro-pDoc-m_Monthm_nCurrentMonth.nProduct; CString str; str.Format(%d,pDoc-m_nTotalProduct); m_Year
11、Pro.SetCaption(str);/显示总产值 pDoc-m_Monthm_nCurrentMonth.nProduct=m_nMonPro; /记录当月产值,并计算增长率 if(m_nCurrentMonth=0) m_strRiseRate=第一月无此值; else if(pDoc-m_Monthm_nCurrentMonth-1.nProduct=0) m_strRiseRate=上月无产值; else m_strRiseRate.Format(%.2f%, (pDoc-m_Monthm_nCurrentMonth.nProduct-pDoc-m_Monthm_nCurrentMo
12、nth-1.nProduct)/(float)(pDoc-m_Monthm_nCurrentMonth-1.nProduct)*100); pDoc-SetModifiedFlag(); UpdateData(false); E、添加输入年份函数OnChangeYear()。使用类向导增加CEx101View类的成员函数OnChangeYear(),响应输入年份控件IDC_YEAR的EN_CHANGE消息。代码如下:void CEx101View:OnChangeYear() CEx101Doc* pDoc=GetDocument(); UpdateData();/获取最新输入 if(pDoc
13、-m_nYear!=m_nYear) /判断数据变化 pDoc-m_nYear=m_nYear;/记录到Doc中 pDoc-SetModifiedFlag();/设置修改标志 F、使用IDC_SPIN_YEAR微调控件改变年份。使用类向导增加CEx101View类的成员函数OnDeltaposSpinYear(),响应改变年份的微调控件的UDN_DELTAPOS消息。代码如下:void CEx101View:OnDeltaposSpinYear(NMHDR* pNMHDR, LRESULT* pResult) NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR;
14、 UpdateData();/获取最新输入 if(pNMUpDown-iDelta0)&(m_nYear0) m_nYear-;/减小年份并且不能小于0 else if(pNMUpDown-iDeltam_nYear=m_nYear;/文档记录年份 GetDocument()-SetModifiedFlag(); UpdateData(FALSE); *pResult = 0;G、添加月份选择处理函数OnSelchangeMonth()。利用类向导在CEx101View类中添加成员函数,响应月份控件IDC_MONTH的CBN_SELCHANGE消息。代码如下:void CEx101View:O
15、nSelchangeMonth() CEx101Doc* pDoc=GetDocument(); /获得当前选择月份并设置正确显示 m_nCurrentMonth=m_ctrlMonthList.GetCurSel(); /计算这个月增长率 if(m_nCurrentMonth=0) /是一月? m_strRiseRate=第一月无此值!; else if(pDoc-m_Monthm_nCurrentMonth-1.nProduct=0) m_strRiseRate=上月无产值; else /计算增长率 m_strRiseRate.Format(%.2f%,(pDoc-m_Monthm_nCu
16、rrentMonth.nProduct-pDoc-m_Monthm_nCurrentMonth-1.nProduct)/ (float)(pDoc-m_Monthm_nCurrentMonth-1.nProduct)*100); /获得当月产值和备注 m_nMonPro=pDoc-m_Monthm_nCurrentMonth.nProduct; m_strMonMem=pDoc-m_Monthm_nCurrentMonth.sNote; UpdateData(FALSE);6、实现CEx101Doc类的串行化函数。CEx101Doc类的串行化函数是MFC自动生成的,添加所需的代码,实现框架传递
17、过来的CArchive对象对数据的读写操作。在这个函数中给出了CArchive的“”操作符、Read()和Write()函数的用法。这里操作的文件就是在创建工程时确定的hjm文件。从这里就可以知道hjm文件的格式。void CEx101Doc:Serialize(CArchive& ar) if (ar.IsStoring() /判断是否存储 /实现存储写的代码 arm_nYear; /写入年份 arm_nTotalProduct; /写入年总产值 for(int i=0;i12;i+) /循环写入各月信息 arm_nYear; /读出年份 arm_nTotalProduct;/读出总产值 i
18、nt nTemp=0; for(int i=0;im_Monthi.nProduct;/这个月的产值 if(m_Monthi.nProduct0)/纠正负值 m_Monthi.nProduct=-m_Monthi.nProduct; nTemp+=m_Monthi.nProduct;/临时统计各月总产值 ar.Read(m_Monthi.sNote,50);/读出该月的备注信息 m_Monthi.sNote49=NULL;/确保字符串结束符存在 /进行检查工作 if(nTemp!=m_nTotalProduct)/各月累计和总产值不符 AfxMessageBox(该报表文件中总产值与各月总和不
19、符,n将总产值置为各月总和); m_nTotalProduct=nTemp; 此时,程序可以编译、链接运行,已经可以对文档,即hjm文件进行新建、打开、保存等操作。7、使用CStdioFile类。使用串行化函数,可以完成对框架支持的文件的固定操作,但在需要实现一些特殊功能时,它就有些捉襟见肘了。从上面实现的程序基础上继续来扩展功能:用CStdioFile类实现从hjm数据文件中来生成txt文本文件。1) 添加菜单资源。选择工作区资源视图,打开Menu项编辑IDR_YEARTYPE菜单。打开“文件”菜单,在菜单项“另存为”和“打印”二者之间加入一个Separator菜单项,然后在加入“生成文本报
20、表”菜单项;ID为ID_CREATE_TEXT,Caption为“生成文本报表”。2) 生成文本报表函数,利用类向导在CEx101Doc类中增加响应ID_CREATE_TEXT的COMMAND消息的成员函数,即完成当前打开的年度产值报表hjm文件生成一个txt文件。在这里使用使用CFile的派生类CStdioFile类来完成。CStdioFile的流方式文本操作对此使用较为方便。该函数还演示了对于文件名信息的一些操作技巧。void CEx101Doc:OnCreateText() CString str; CString filename=GetPathName();/获取该文档当前的hjm名
21、 if(filename.IsEmpty() /如果空 filename=GetTitle()+.txt;/从标题获得 else filename=filename.Left(filename.GetLength()-3)+txt; CStdioFile file; /用CStdioFile创建一个txt文件,注意使用的文件操作标志 if(file.Open(filename,CFile:modeCreate|CFile:modeWrite|CFile:typeText)=0) str=创建文件+filename+失败!; AfxMessageBox(str);/创建失败 return; /写
22、文本文件内容 str.Format(%d年产值报告nn,m_nYear); file.WriteString(str); str.Format(总产值:%d元nn,m_nTotalProduct); file.WriteString(str); str.Format(月份: 产值: 单位(元) 备注:n); file.WriteString(str); for(int i=0;i12;i+) str.Format(%4d %13d %sn,i+1,m_Monthi.nProduct,m_Monthi.sNote); file.WriteString(str); /文件写完了,再做一些辅助工作 file.SetLength(file.GetPosition(); file.Close(); str=notepad +filename; WinExec(str,SW_SHOW);/用记事本打开编译,链接并运行程序,打开“文件”文件菜单选择“生成文本报表”,观察运行效果。思考和练习1. 什么是“串行化”?VC+中是通过什么成员函数实现这一功能的?2. CFile类的哪几个函数具备文件定位的功能?3. 怎样用CFile类和CFileDialog类配合使用来完成文件的读写操作?
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1