




已閱讀5頁,還剩24頁未讀, 繼續(xù)免費(fèi)閱讀
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
第8章 文檔序列化大多數(shù)應(yīng)用程序都為用戶提供了數(shù)據(jù)的保存功能,這些數(shù)據(jù)可能是電子表格、字處理文檔、一組數(shù)據(jù)或圖形等等。從磁盤存儲器上存取這些數(shù)據(jù)的工作往往是通過文件操作或者數(shù)據(jù)庫操作來完成的。關(guān)于數(shù)據(jù)庫操作的內(nèi)容,我們將在后面的章節(jié)中進(jìn)行詳細(xì)的介紹,在本章的內(nèi)容中,我們主要討論如何通過文件操作實(shí)現(xiàn)一般意義上的數(shù)據(jù)存取工作。通過文件操作來實(shí)現(xiàn)數(shù)據(jù)的存取工作通常有兩種工作方式:一種是使對象具有序列化;另一種方法就是直接使用CFile對象處理文件。本章就將對這兩部分內(nèi)容分別做出詳解。8.1 序列化在MFC當(dāng)中,對象的序列化功能主要是通過文檔/視圖結(jié)構(gòu)中特有的文檔對象的序列化機(jī)制來實(shí)現(xiàn)的。本節(jié),我們將詳細(xì)介紹如何使用序列化機(jī)制來實(shí)現(xiàn)對象的序列化。序列化,簡單地說就是向一個(gè)持久性的存儲媒體如磁盤文件保存對象或讀取對象的過程。序列化分為兩部分,當(dāng)把應(yīng)用程序數(shù)據(jù)以文件形式存儲在系統(tǒng)磁盤中時(shí),叫做序列化;當(dāng)從磁盤文件中恢復(fù)應(yīng)用程序數(shù)據(jù)的狀態(tài)時(shí),叫做反序列化,這兩個(gè)部分的組合構(gòu)成了Visual C+中的應(yīng)用程序?qū)ο蟮男蛄谢?.1.1 CArchive類和Serialize函數(shù)Visual C+應(yīng)用程序中的序列化是通過CArchive類來實(shí)現(xiàn)的。CArchive類總是與一個(gè)CFile對象相關(guān)聯(lián),CArchive類是作為CFile對象的輸入輸出流而設(shè)計(jì)的,如圖8-00所示,它使用經(jīng)過重載的C+流入()操作符從存儲應(yīng)用程序數(shù)據(jù)的文件中實(shí)現(xiàn)讀取和寫入數(shù)據(jù),而將這些數(shù)據(jù)保存到磁盤文件中的工作由CArchive對象指示CFile對象來完成。應(yīng)用程序框架應(yīng)用程序?qū)ο?序列化函數(shù)CFile CArchive類圖8-00 CArchive類和CFile類可以實(shí)現(xiàn)序列化的類即從CObject繼承而來的類,有一個(gè)叫做Serialize的成員函數(shù),序列化工作主要是在這個(gè)函數(shù)當(dāng)中進(jìn)行的。當(dāng)應(yīng)用程序讀取或?qū)懭胛募r(shí),文檔對象的Serialize函數(shù)被調(diào)用,并傳遞用于從文件讀取或向文件寫入數(shù)據(jù)的CArchive對象。在Serialize函數(shù)中,要遵循的典型邏輯是通過調(diào)用CArchive類的IsStoring或IsLoading函數(shù)來判定當(dāng)前行為是正在對文件寫入還是讀取。根據(jù)這兩個(gè)函數(shù)中任何一個(gè)的返回值即可判定應(yīng)用程序需要從CArchive類的I/O流中讀取還是向其寫入。當(dāng)用戶在打開或保存擁有文檔對象數(shù)據(jù)的文件或者使用文檔對象的Open、Save、Save As菜單命令時(shí),MFC便會自動調(diào)用Serialize函數(shù),一個(gè)典型的Serialize函數(shù)如下所示:void CAge:Serialize( CArchive& ar ) CObject:Serialize( ar ); if( ar.IsStoring() ) ar m_years; 其中,ar是一個(gè)指明應(yīng)用程序序列化對象的CArchive引用參數(shù)。CArchive:Serialize成員函數(shù)可以告訴用戶序列化對象當(dāng)前是否用來存儲或加載??梢詫erialize函數(shù)放置在所創(chuàng)建的任何類中,以便文檔的Serialize函數(shù)中調(diào)用這些類的Serialize函數(shù)。8.1.2 使自己的類支持序列化在前幾章講過的例子中使用CString類的字符串來保存文本行,由于它是MFC類,因此可以串行化自己,將自己寫入磁盤或從磁盤文件中讀取二進(jìn)制數(shù)據(jù)來建立對象。那么,如果不是標(biāo)準(zhǔn)的MFC類,比如用戶自己定義的類,如何讓它支持序列化呢?要讓用戶定義的類支持序列化,一般分為五步:1.從CObject或其派生類派生出用戶的類2.在類聲明文件中,加入DECLARE_SERIAL宏。編譯時(shí),編譯器將擴(kuò)充該宏,這是串行化對象所必需的。3.重載Serialize()成員函數(shù),加入必要的代碼,用以保存對象的數(shù)據(jù)成員到CArchive對象以及從CArchive對象載入對象的數(shù)據(jù)成員狀態(tài)。4.定義一個(gè)不帶參數(shù)的構(gòu)造函數(shù)。5.在實(shí)現(xiàn)文件中加入IMPLEMENT_SERIAL宏。下面將通過一個(gè)實(shí)例來演示如何讓用戶定義的類支持序列化功能。8.1.3 實(shí)例:保存和顯示圖形還記得第6章的繪圖程序嗎,用戶畫好的圖形不僅不能保存下來,而且當(dāng)窗口發(fā)生重繪時(shí),圖形也就不見了,本實(shí)例就將解決這兩個(gè)問題,不僅使所畫的圖形在窗口重繪時(shí)依然保留,而且還給它添加了保存及再顯示功能。我們在第6章繪圖程序上加的內(nèi)容夠多了,這里為了更清晰的講述本章的重點(diǎn)序列化,將新建一個(gè)工程,當(dāng)然,這個(gè)工程所要實(shí)現(xiàn)的功能還是和第6章繪圖程序一樣,只不過給它加個(gè)序列化,完整例程請參見光盤中的例子代碼EX08_00,具體操作步驟如下:l 步驟1:新建一個(gè)MFC單文檔應(yīng)用程序,工程名為EX08_00或用戶自定義。l 步驟2:為新建的工程先實(shí)現(xiàn)第6章的簡單繪圖功能(詳細(xì)步驟請參見第6章)。1. 在資源面板中修改原來的標(biāo)準(zhǔn)菜單,新插入一個(gè)菜單名為“繪圖”,下面有四個(gè)菜單項(xiàng)“點(diǎn)”、“線”、“矩形”、“橢圓”,修改它們的ID分別為:ID_GRAPH_DOT、ID_GRAPH_LINE、ID_GRAPH_RECTANGLE、ID_GRAPH_ELLIPSE。2. 在CEX08_00View類中添加兩個(gè)成員變量CPoint m_ptOrigin和int m_nType,分別表示繪圖的起點(diǎn)和繪圖的類型,并在構(gòu)造函數(shù)中初始化為0和-1。3. 在CEX08_00View中加入四個(gè)菜單項(xiàng)“點(diǎn)”、“線”、“矩形”和“橢圓”的WM_COMMAND消息的響應(yīng)函數(shù)OnGraphDot、OnGraphLine、OnGraphRectangle 、OnGraphEllipse,在消息響應(yīng)函數(shù)中設(shè)置變量m_nType的值。m_nType為0,表示畫點(diǎn);m_nType為1,表示畫線;m_nType為2,表示畫矩形;m_nType為3,表示畫橢圓。4. 在CEX08_00View類中加入WM_LBUTTONDOWN和WM_LBUTTONUP的消息響應(yīng)函數(shù)OnLButtonDown和OnLButtonUp。在OnLButtonDown中保存鼠標(biāo)按下的點(diǎn);在OnLButtonUp中,根據(jù)m_nType的值畫相應(yīng)的圖形。l 步驟3:給工程添加一個(gè)可序列化的類CGraph。1 新建一個(gè)類CGraph,從CObject派生。打開工作臺ClassView頁面,鼠標(biāo)右擊最頂層的EX08_00 classes,在彈出的快捷方式菜單中選擇New Class,在彈出的New Class對話框上,在Class type中要選Generic Class,在類名Name中輸入CGraph,單擊Base class下面列表框中Derived From下面高亮顯示的第一欄,輸入將要派生的基類CObject,后面類型為publice,如圖8-01所示,然后單擊OK。圖8-01 添加新類CGraph當(dāng)單擊OK按鈕添加該類時(shí),會彈出一個(gè)如圖8-02所示的對話框,該對話框提示Class Wizard無法為從CObject派生出的CGraph類找到合適的頭文件。我們不用理會它,在此消息框中單擊確定按鈕即可,因?yàn)楹线m的頭文件已經(jīng)包含在CGraph類中。2 在CGraph類中重載Serialize()成員函數(shù)。我們要實(shí)現(xiàn)序列化,先對其進(jìn)行改造,在工作臺的ClassView頁面中選擇CGraph類,單擊鼠標(biāo)右鍵,選擇Add Member Function增加一個(gè)成員函數(shù),在彈出的對話框中Functiong Type中輸入void,在Function Declaretion編輯框中輸入 Serialize(CArchive& ar),然后選擇Public,按OK即可。然后在ClassView中可以看到這個(gè)函數(shù)。圖8-02 創(chuàng)建新類時(shí)的警告信息3. 在類CGraph的頭文件中,加入DECLARE_SERIAL宏,代碼如下:class CGraph : public CObject public:void Serialize(CArchive& ar);CGraph();virtual CGraph();DECLARE_SERIAL(CGraph);要對CGraph類實(shí)現(xiàn)序列化,需要在類的.h文件中加入宏DECLARE_SERIAL的調(diào)用,這個(gè)宏不需要加分號,并且后面有一個(gè)參數(shù)表示添加序列化特性的類名。4 定義一個(gè)不帶參數(shù)的構(gòu)造函數(shù)。打開工作臺的CGraph類,可以看到,不帶參數(shù)的構(gòu)造函數(shù)已經(jīng)存在于類中了,這是我們最開始創(chuàng)建CGraph這個(gè)新類時(shí)自動添加的。MFC在從磁盤文件載入對象狀態(tài)并重建對象時(shí),需要有一個(gè)缺省的不帶任何參數(shù)的構(gòu)造函數(shù)。序列化對象將用該構(gòu)造函數(shù)生成一個(gè)對象,然后調(diào)用Serialize()函數(shù),用重建對象所需的值來填充對象的所有數(shù)據(jù)成員變量。5 在類CGraph實(shí)現(xiàn)文件中加入IMPLEMENT_SERIAL宏。打開類CGraph的實(shí)現(xiàn)文件,在該類的構(gòu)造函數(shù)前添加MPLEMENT_SERIAL宏,代碼如下:/ Graph.cpp: implementation of the CGraph class./#include stdafx.h#include EX08_00.h#include Graph.h#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE=_FILE_;#define new DEBUG_NEW#endif/ Construction/Destruction/IMPLEMENT_SERIAL(CGraph,CObject,1)CGraph:CGraph()可見,將該宏的調(diào)用添加在構(gòu)造函數(shù)前,也不需要分號。IMPLEMENT_SERIAL宏用于定義一個(gè)從CObject派生的可序列化類的各種函數(shù)。宏的第一和第二個(gè)參數(shù)分別代表可序列化的類名和該類的直接基類。第三個(gè)參數(shù)是對象的版本號,它是一個(gè)大于或等于零的整數(shù)。MFC序列化代碼在將對象讀入內(nèi)存時(shí)檢查版本號。如果磁盤文件上的對象的版本號和內(nèi)存中的對象的版本號不一致,MFC將拋出一個(gè)CArchiveException異常,阻止程序讀入一個(gè)不匹配版本的對象?,F(xiàn)在,我們就可以象使用標(biāo)準(zhǔn)MFC類一樣使用CGraph的序列化功能了。l 步驟4:構(gòu)造CGraph類,做準(zhǔn)備工作。1. 在類CGraph的頭文件中添加三個(gè)成員變量m_ptOrigin、m_ptEnd、m_nType。我們既然想保存下來所畫的圖形,那么至少要保留住關(guān)于這些圖形的一些信息,不管用戶畫的是線、矩形還是橢圓,它們都有一個(gè)共同點(diǎn):就是由兩點(diǎn)決定這個(gè)圖形,那么從起點(diǎn)到終點(diǎn)畫的到底是什么圖形,就要看m_nType的值了,因此這里定義了兩個(gè)CPoint型的變量,用于保存用戶所畫的一組圖形的各個(gè)起點(diǎn)和終點(diǎn);另一個(gè)為int型變量用來指定所畫的每個(gè)圖形的類型。class CGraph : public CObject public:CPoint m_ptOrigin; /記錄起始點(diǎn)CPoint m_ptEnd; /記錄終點(diǎn)int m_nType; /記錄畫圖類型void Serialize(CArchive& ar);CGraph();virtual CGraph();DECLARE_SERIAL(CGraph);2. 給類CGraph添加一個(gè)帶參數(shù)的構(gòu)造函數(shù)鼠標(biāo)右擊CGraph類,在彈出的快捷方式菜單中選擇Add Member Function,函數(shù)類型(Function Type)編輯框中什么都不填,因?yàn)闃?gòu)造函數(shù)沒有返回值,函數(shù)聲明(Function Declaration)為CGraph(int m_drawType,CPoint m_ptFrom, CPoint m_ptTo),編輯這個(gè)帶參數(shù)的構(gòu)造函數(shù),添加如下代碼:CGraph:CGraph(int m_drawType, CPoint m_ptFrom, CPoint m_ptTo)m_nType=m_drawType;m_ptOrigin=m_ptFrom;m_ptEnd=m_ptTo;在這個(gè)對象構(gòu)造函數(shù)中,用傳遞到構(gòu)造函數(shù)的畫圖類型、起點(diǎn)和終點(diǎn)三個(gè)參數(shù)來初始化類CGraph中相應(yīng)意義的三個(gè)變量。l 步驟5:在文檔類CEX08_00Doc中定義一個(gè)成員變量,用于保存每一個(gè)圖形對象?,F(xiàn)在已經(jīng)有了一個(gè)可以用來表示用戶所繪圖形的對象,那么接下來的重點(diǎn)是如何將這些對象保存下來。當(dāng)用戶畫一個(gè)圖形,就產(chǎn)生一個(gè)這樣的圖形對象,因此,這個(gè)對象是動態(tài)的不斷增長的。CObArray類是一個(gè)對象數(shù)組類,它可以動態(tài)調(diào)整自己的大小以適應(yīng)放在它里面的元素的個(gè)數(shù)。它可以存放任何從CObject類派生出的對象(如前面的CGraph對象),它的大小只受系統(tǒng)的內(nèi)存空間的限制。MFC中其他動態(tài)數(shù)組類包括CStringArray、CByteArray、CWordArray、CDWordArray、CPtrArray,它們的不同之處在于存放的對象的類型。這里,我們鼠標(biāo)右擊CEX08_00Doc類,在彈出的快捷方式菜單中選擇Add Member Variable,變量類型為CObArray,變量名為m_obArray。l 步驟6:將用戶畫的每一個(gè)圖形對象保存到m_obArray中。要將圖形對象保存到對象數(shù)組m_obArray中,首先就要得到對象的繪圖類型和起點(diǎn)、終點(diǎn),然后創(chuàng)建一個(gè)新的圖形對象,并把它加入到對象數(shù)組m_obArray中。我們知道,當(dāng)用戶按下鼠標(biāo)左鍵,然后隨之拖動出一個(gè)圖形,最后當(dāng)鼠標(biāo)抬起的時(shí)候,那么就是這個(gè)圖形對象生成的時(shí)候,因此,應(yīng)該在CEX08_00View:OnLButtonUp函數(shù)中保存圖形對象。在OnLButtonUp函數(shù)中的原來代碼的尾部添加如下代碼:void CEX08_00View:OnLButtonUp(UINT nFlags, CPoint point) CGraph *pGraph=new CGraph(m_nType,m_ptOrigin, point);GetDocument()-m_obArray.Add(pGraph);CView:OnLButtonUp(nFlags, point);這段代碼中,首先聲明一個(gè)CGraph類的一個(gè)指針對象,并且用該類的帶參數(shù)的構(gòu)造函數(shù)來構(gòu)建這個(gè)對象,構(gòu)造函數(shù)中的參數(shù)就是OnLButtonUp函數(shù)中用于畫圖的類型和起點(diǎn)、終點(diǎn)。用戶再回頭看一看CGraph類中帶參數(shù)的構(gòu)造函數(shù),就會明白,實(shí)際上構(gòu)造函數(shù)傳遞過來的參數(shù)是為了初始化CGraph類的三個(gè)成員變量m_ptOrigin、m_ptEnd、m_nType,這三個(gè)成員變量構(gòu)成了一個(gè)CGraph類對象,然后將該對象通過m_obArray.Add保存到對象數(shù)組中。由于對象數(shù)組是在文檔類中定義的,在視圖類中不能直接引用,因此,前邊需要調(diào)用GetDocument函數(shù)來取得訪問文檔類的權(quán)利。最后別忘了在視圖類的實(shí)現(xiàn)文件中添加#include Graph.h,將類CGraph的頭文件包含進(jìn)來。l 步驟7:在CGraph類中完成繪圖功能。CGraph類對象中包含著三個(gè)重要的畫圖參數(shù),因此,這個(gè)對象是可以繪制自身的,當(dāng)視圖類需要重繪圖形時(shí),它只需要向該類發(fā)送一條消息,告訴它要繪制自己就可以了。鼠標(biāo)右擊CGraph類,選擇Add Member Function,函數(shù)類型(Function Type)為void,函數(shù)聲明(Function Declaration)為Draw(CDC* pDC),在該函數(shù)中添加如下代碼:void CGraph:Draw(CDC *pDC)switch(m_nType)case 0:pDC-SetPixel(m_ptEnd.x,m_ptEnd.y,RGB(255,0,0);break;case 1:pDC-MoveTo(m_ptOrigin);pDC-LineTo(m_ptEnd);break;case 2:pDC-Rectangle(m_ptOrigin.x,m_ptOrigin.y,m_ptEnd.x,m_ptEnd.y);break;case 3:pDC-Ellipse(m_ptOrigin.x,m_ptOrigin.y,m_ptEnd.x,m_ptEnd.y);break;default:break;在CGraph類中繪圖,Draw函數(shù)中用到三個(gè)繪圖參數(shù)就是CGraph類的三個(gè)成員變量。l 步驟8:在CEX08_00View:OnDraw函數(shù)中繪圖,在OnDraw函數(shù)中添入如下代碼:void CEX08_00View:OnDraw(CDC* pDC)CEX08_00Doc* pDoc = GetDocument();ASSERT_VALID(pDoc);/ TODO: add draw code for native data hereif (pDoc-m_obArray.GetSize()for(int i=0;im_obArray.GetSize();i+)(CGraph*)pDoc-m_obArray.GetAt(i)-Draw(pDC);在這個(gè)函數(shù)中,首先判斷數(shù)組對象中是否有元素,即是否有圖形需要繪制,如果沒有就什么都不做,如果有元素,那么就通過for循環(huán)從文檔類的數(shù)組對象中依次取出圖形對象(包括三個(gè)重要繪圖參數(shù)),然后調(diào)用圖形對象的Draw函數(shù),即第7步驟中的Draw函數(shù)。到現(xiàn)在,這個(gè)繪圖程序就具備了重繪的功能,無論窗口怎樣改變,用戶所繪制的圖形依然顯示在窗口上。下面我們繼續(xù)給它添加保存和再顯示的功能。l 步驟9:保存和再顯示圖形。1. 打開CEX08_00Doc:Serialize函數(shù),將原來的代碼刪除,添加如下代碼:void CEX08_00Doc:Serialize(CArchive& ar)m_obArray.Serialize(ar);這里利用了CObArray類的功能。在文檔類的Serialize函數(shù)中調(diào)用對象數(shù)組的Serialize函數(shù),該對象數(shù)組將會把指令向下傳遞到對象數(shù)組中,并調(diào)用每個(gè)對象的Serialize函數(shù)。因此,下面我們將完善CGraph類的Serialize函數(shù)。2. 完善CGraph:Serialize()函數(shù),代碼如下:void CGraph:Serialize(CArchive &ar)if (ar.IsStoring()arm_nTypem_ptOriginm_nTypem_ptOriginm_ptEnd;在上面的代碼中我們用到了和和m_nTypem_ptOriginm_ptEnd;這一句,其中表示從ar中讀出數(shù)據(jù)m_nType, m_ptOrigin和 m_ptEnd,這個(gè)符號及可以連用,亦可以分開來用,如ar m_nType;ar m_ptOrigin;同樣arm_nTypem_ptOriginm_ptEnd;中的是把數(shù)據(jù)存入ar中。需要注意的是,三個(gè)變量讀取和保存的順序一定要和在類CGraph中帶參數(shù)的構(gòu)造函數(shù)中三個(gè)參數(shù)的聲明順序是一致的。現(xiàn)在編譯運(yùn)行這個(gè)程序,隨意畫幾個(gè)圖形,然后選擇菜單“文件”|“保存”,在彈出的“保存”對話框中輸入文件名將所畫圖形保存到文件中,然后關(guān)閉應(yīng)用程序,再重新運(yùn)行該應(yīng)用程序,選擇 “文件”|“打開” 菜單,在彈出的“打開”對話框上雙擊先前保存有圖形的文件,就可以看到先前保存的圖形再一次顯示在窗口上了。8.1.4 刪除文檔數(shù)據(jù)當(dāng)用戶單擊“打開”和“新建”菜單項(xiàng)或按鈕時(shí)會自動調(diào)用DeleteContents虛函數(shù),這個(gè)時(shí)候是刪除文檔數(shù)據(jù)的最好時(shí)機(jī)。下面將以上一小節(jié)的例子EX08_00工程為基礎(chǔ),講述如何刪除文檔數(shù)據(jù)。打開工作臺的Class View頁面,鼠標(biāo)右擊CEX08_00Doc類,在彈出的快捷方式菜單中選擇Add Virtual Function,在彈出的對話框上左邊的列表框中找到DeleteContents函數(shù),雙擊該函數(shù)將其加入到文檔類中,用戶可以在這個(gè)函數(shù)中刪除文檔數(shù)據(jù)。通常,用戶在刪除文檔數(shù)據(jù)時(shí),經(jīng)常犯以下兩種錯(cuò)誤:1錯(cuò)誤方法一:for (int i=0;im_obArray.GetSize();i+)delete (CGraph*)m_obArray.GetAt(i);m_obArray.RemoveAll();原因:每循環(huán)一次,m_obArray.GetSize()返回的值都會減小,造成數(shù)據(jù)的漏刪。2錯(cuò)誤方法二:int index= m_obArray.GetSize();for (int i=0;iindex;i+)delete (CGraph *) m_obArray.GetAt(i); m_obArray.RemoveAt(i);原因:每刪除一個(gè)數(shù)組元素,數(shù)組都會重新排序,它的下標(biāo)會變。正確的刪除方法應(yīng)按如下方式:void CEX08_00Doc:DeleteContents() / TODO: Add your specialized code here and/or call the base classint index=m_obArray.GetSize();while(index-)delete (CGraph*)m_obArray.GetAt(index);m_obArray.RemoveAll();CDocument:DeleteContents();最后不要忘了在文檔類CEX08_00Doc實(shí)現(xiàn)文件的前面添加#include Graph.h語句,將類CGraph的頭文件包含進(jìn)來。序列化簡化了對象的保存和載入,為對象提供了持久性。但是,序列化本身仍具有一定的局限性。由于序列化一次從文件中載入所有對象,因此,它不適合于大文件編輯器和數(shù)據(jù)庫。對于數(shù)據(jù)庫和大文件編輯器,它們每次只是從文件中讀入一部分。此時(shí),就不應(yīng)該采用文檔的序列化機(jī)制來直接讀取和保存文件了。另外,使用外部文件格式(預(yù)先定義的文件格式而不是本應(yīng)用程序定義的文件格式)的程序一般也不使用文檔的序列化。8.2 CFile類文件的處理包括創(chuàng)建及打開文件、數(shù)據(jù)讀取、關(guān)閉文件等一系列操作。在MFC中,包含文件相關(guān)操作的基類為CFile類。CFile類是從CObject類派生而來的,CFile類用來處理正常文件的I/O操作,它直接供無緩沖的、二進(jìn)制磁盤輸入/輸出服務(wù),并且通過其派生類間接支持文本文件和內(nèi)存文件。8.2.1 CFile類的構(gòu)造函數(shù)CFile類有三個(gè)構(gòu)造函數(shù),其原型如下:1. CFile();2. CFile(int nFile);3. CFile( LPCTSTR lpszFileName, UINT nOpenFlags );第一種方式,在聲明CFile實(shí)例后,不需要設(shè)定任何參數(shù)。第二種方式,有一個(gè)參數(shù)nFile為一個(gè)已打開文件的句柄,就是建立已打開文件的另一個(gè)實(shí)例,它不做任何錯(cuò)誤檢查,直接用構(gòu)造函數(shù)打開。第三種方式,參數(shù)lpszFileName指定想要操作的文件的名稱和路徑的字符串,路徑可以是相對的或絕對的;參數(shù)nOpenFlags指定文件的共享和存取方式,該參數(shù)可以用OR來組合下面所列的值:l CFile:modeCreate指示構(gòu)造函數(shù)創(chuàng)建一個(gè)新文件,如果該文件已存在,則該文件截短為0l CFile:modeRead 打開文件只用于讀l CFile:modeReadWrite 打開文件用于讀寫l CFile:modeWrite 打開文件只用于寫l CFile:modeNoInberit 阻禁止該文件被子進(jìn)程繼承l(wèi) CFile:shareDenyNone 打開文件,不允許其它進(jìn)程讀或?qū)懺撐募?。如果該文件已由其它任何進(jìn)程用兼容方式打開,則Create將失敗l CFile:shareDenyWrite 打開文件,不允許其它進(jìn)程寫該文件。如果該文件已由其它任何進(jìn)程用兼容方式或?qū)懛绞酱蜷_,則Create將失敗l CFile:shareDenyRead 打開文件,不允許其它進(jìn)程讀該文件。如果該文件已由其它任何進(jìn)程用兼容方式或讀方式打開,則Create將失敗l CFile:shareExclusive 以獨(dú)占方式打開文件,不允許其它進(jìn)程寫該文件。如果該文件已用其它讀或?qū)懛绞酱蜷_,即使是當(dāng)前進(jìn)程打開,則構(gòu)造也將失敗l CFile:shareCompat 以兼容方式打開文件,允許給定機(jī)器上任何進(jìn)程打開該文件任意次。如果該文件已用其它任何共享方式打開,構(gòu)造將失敗l CFile:typeText 設(shè)置文本方式,對回車換行進(jìn)行特殊處理,它只用于派生類l CFile:typeBinary 設(shè)置二進(jìn)制方式,它只用于派生類以上描述了文件共享和存取標(biāo)志。在第三種構(gòu)造函數(shù)中,一個(gè)存取許可和一個(gè)共享選項(xiàng)是必需的。modeCreate 和modeNoInberit方式是可選的。8.2.2 打開文件CFile類用Open函數(shù)來創(chuàng)建和打開文件,用Open創(chuàng)建新文件,必須有一個(gè)文件名,并且選擇一定的打開方式,函數(shù)原形如下:virtual BOOL Open( LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError = NULL);第一個(gè)參數(shù)lpszFileName指定想要操作的文件的名稱和路徑的字符串。第二個(gè)參數(shù)nOpenFlags指定文件的共享和存取方式,用法同CFile類第三種構(gòu)造函數(shù)中的nOpenFlags參數(shù)用法相同。第三個(gè)參數(shù)pError,表示文件異常處理對象的指針,當(dāng)文件打開失敗時(shí),系統(tǒng)會將文件異常消息存放在CFileException打開文件的m_cause數(shù)據(jù)成員。用戶可以根據(jù)調(diào)用Open函數(shù)后的返回值來簡單的判斷文件是否正常打開或創(chuàng)建。若用戶想了解是什么原因造成打開文件失敗時(shí),就必須指定CFileException對象,該對象專門處理文件操作上的問題。在CFileException對象內(nèi)的m_cause數(shù)據(jù)成員便負(fù)責(zé)記錄文件操作到底出了哪些問題。8.2.3 讀寫數(shù)據(jù)文件創(chuàng)建及打開后,就可以對數(shù)據(jù)進(jìn)行讀取或?qū)懭肓恕File對磁盤文件的定點(diǎn)、讀取和寫入操作分別通過函數(shù)Write,Read和Seek進(jìn)行的。1. 寫入數(shù)據(jù)Write函數(shù)virtual void Write( const void* lpBuf, UINT nCount );該函數(shù)用于將緩沖區(qū)數(shù)據(jù)寫到文件中,參數(shù)lpBuf指向用戶定義的緩沖區(qū)的指針,該參數(shù)中包含用戶將要寫到文件中的數(shù)據(jù);參數(shù)nCount表示要向文件中寫入的最大字節(jié)數(shù)。2. 讀取數(shù)據(jù)Read函數(shù)virtual UINT Read( void* lpBuf, UINT nCount );該函數(shù)從文件中讀取數(shù)據(jù)到緩沖區(qū)里,參數(shù)lpBuf指向用戶定義的緩沖區(qū)的指針,用來接收數(shù)據(jù);參數(shù)nCount表示要從文件中讀出的最大字節(jié)數(shù)。3. 定位文件指針到文件的指定位置Seek函數(shù)virtual LONG Seek( LONG lOff, UINT nFrom );該函數(shù)用于定位文件指針位置,如果所請求的位置合法,則返回值為距離文件頭的新字節(jié)偏移。第一個(gè)參數(shù)lOff,表示指針偏移的字節(jié)數(shù)。第二個(gè)參數(shù)nFrom,表示偏移的起點(diǎn),該參數(shù)必須是下列值中的唯一:l CFile:begin表示從文件的頭部開始偏移指針l CFile:current表示從文件的當(dāng)前位置開始偏移指針l CFile:end表示從文件的尾部開始偏移指針8.2.4 關(guān)閉文件文件的打開和關(guān)閉是相對的,打開一個(gè)文件之后,必須把它關(guān)閉,關(guān)閉文件可以調(diào)用Close函數(shù),原形如下:virtual void Close( );8.3 文件I/O處理實(shí)現(xiàn)文件操作的方法有很多,用戶不僅可以通過先前學(xué)過的CFile類來進(jìn)行文件操作,還可以通過C或C+函數(shù)、API函數(shù)來實(shí)現(xiàn),更可以通過創(chuàng)建打開和保存對話框來實(shí)現(xiàn)文件操作。本章將分別介紹這幾種文件操作的方法及如何創(chuàng)建打開和保存對話框。8.3.1 利用MFC類來實(shí)現(xiàn)這里將通過一個(gè)例子來介紹如何通過定義類CFile對象來實(shí)現(xiàn)文件的I/O操作,完整例程請參見光盤中例子代碼EX08_01,具體操作步驟如下:l 步驟1:新建一個(gè)MFC單文檔應(yīng)用程序,工程名為EX08_01或用戶自定義。l 步驟2:給工程新添加一個(gè)菜單,名為“文件操作”,在下面再添加一個(gè)級聯(lián)菜單名為“利用MFC類”,然后給這個(gè)級聯(lián)菜單添加兩個(gè)菜單項(xiàng)分別為“寫入數(shù)據(jù)”和“讀取數(shù)據(jù)”,ID分別為ID_FILEIO_MFC_WRITE和ID_FILEIO_MFC_READ。結(jié)果如圖8-03所示。圖8-03 添加菜單“利用MFC類”l 步驟3:在CEX08_01sView類中添加上述兩個(gè)菜單項(xiàng)的COMMAND命令消息處理函數(shù),分別為OnFileioMfcRead和OnFileioMfcWrite,并添加如下代碼:1. 在消息處理函數(shù)OnFileioMfcWrite中寫入數(shù)據(jù)void CEX08_01View:OnFileioMfcWrite() / TODO: Add your command handler code hereCFile file(c:example1.txt,CFile:modeCreate|CFile:modeWrite);char szchar20=利用MFC類來實(shí)現(xiàn);file.Write(szchar,strlen(szchar);MessageBox(數(shù)據(jù)已寫入);file.Close();2. 在消息處理函數(shù)OnFileioMfcRead中讀取數(shù)據(jù)void CEX08_01View:OnFileioMfcRead() / TODO: Add your command handler code hereCFile file(c:example1.txt,CFile:modeRead);char szchar20;memset(szchar,0,20);file.Read(szchar,20);MessageBox(szchar);file.Close();其中,函數(shù)memset是將緩沖區(qū)用指定的字符填充,原形如下:void *memset( void *dest, int c, size_t count );第一個(gè)參數(shù)dest表示要被填充的緩沖區(qū)。第二個(gè)參數(shù)c表示要填充的字符。第三個(gè)參數(shù)count,填充的長度。本例中用0字符將整個(gè)緩沖區(qū)填滿的用意是防止在讀取文件數(shù)據(jù)時(shí)在正常數(shù)據(jù)的后面出現(xiàn)亂碼。因?yàn)椋覀儾⒉恢缹⒁x取多少個(gè)字符,因此,只好將緩沖區(qū)中的所有字符讀出來,而這個(gè)緩沖區(qū)中沒有被寫入數(shù)據(jù)的那部分內(nèi)存中的內(nèi)容是不定的,讀出來會是亂碼,因此,在往這個(gè)緩沖區(qū)讀入數(shù)據(jù)之前,先將它清0,用0字符填充的原因很簡單,在C語言中,一個(gè)字符串的結(jié)束標(biāo)志就是0字符。8.3.2 利用C函數(shù)來實(shí)現(xiàn)在工程EX08_01的基礎(chǔ)上再添加一個(gè)級聯(lián)菜單名為“利用C函數(shù)”,然后給這個(gè)級聯(lián)菜單添加同樣的兩個(gè)菜單項(xiàng)分別為“寫入數(shù)據(jù)”和“讀取數(shù)據(jù)”,ID分別為ID_FILEIO_C_WRITE和ID_FILEIO_C_READ。結(jié)果如圖8-04所示。圖8-04添加菜單“利用C函數(shù)”在CEX08_01View類中添加上述兩個(gè)菜單項(xiàng)的COMMAND命令消息處理函數(shù),分別為OnFileioCRead和OnFileioCWrite,并添加如下代碼:1. 在消息處理函數(shù)OnFileioCWrite中寫入數(shù)據(jù)void CEX08_01View:OnFileioCWrite() / TODO: Add your command handler code hereFILE *pFile;char szchar20=利用C函數(shù)來實(shí)現(xiàn);pFile=fopen(c:example2.txt,w);fwrite(szchar,1,strlen(szchar),pFile);MessageBox(數(shù)據(jù)已寫入);fclose(pFile);其中fopen是C語言中用于打開文件的函數(shù),原形如下:FILE *fopen( const char *filename, const char *mode );該函數(shù)返回一個(gè)指向FILE文件類型的指針,該函數(shù)有兩個(gè)參數(shù),第一個(gè)參數(shù)filename表示要打開的文件名;第二個(gè)參數(shù)mode表示打開方式,打開方式有如下幾個(gè)選擇:r 打開文件用于讀取數(shù)據(jù)w 打開文件用于寫入數(shù)據(jù)a打開文件用于在文件尾部寫數(shù)據(jù),并不覆蓋前面的數(shù)據(jù)r+打開文件即可以讀又可以寫,前提是該文件必須存在w+ 打開一個(gè)空文件即可以讀又可以寫,如果文件存在,則文件的內(nèi)容將被清除a+打開文件可以讀取并且在尾部可以添加數(shù)據(jù)函數(shù)fwrite用于向文件中寫入數(shù)據(jù),原形如下:size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );第一個(gè)參數(shù)buffer是用戶定義的緩沖區(qū)的指針,該參數(shù)中包含用戶將要寫到文件中的數(shù)據(jù);第二個(gè)參數(shù)size和第三個(gè)參數(shù)count是相輔相成的,代碼中的下面語句fwrite(szchar,1,strlen(szchar),pFile);也可以寫成如下形式;fwrite(szchar,strlen(szchar),1,pFile);假設(shè)用戶定義的緩沖區(qū)大小為20個(gè)字節(jié),那么如果按照第一種代碼形式,表示需要一個(gè)緩沖區(qū)(size為1),緩沖區(qū)大小為20個(gè)字節(jié)(count為20);如果按照第二種代碼形式,表示需要20個(gè)緩沖區(qū)(size為20),每個(gè)緩沖區(qū)大小為1個(gè)字節(jié)(count為1)。第四個(gè)參數(shù)stream指向FILE文件類型的指針。2. 在消息處理函數(shù)OnFileioCRead中讀取數(shù)據(jù)void CEX08_01View:OnFileioCRead() / TODO: Add your command handler code hereFILE *pFile;char szchar20;memset(szchar,0,20);pFile=fopen(c:example2.txt,r);fread(szchar,1,20,pFile);MessageBox(szchar);fclose(pFile);其中fread是C語言中用來從文件中讀取數(shù)據(jù)的函數(shù),原形如下:size_t fread( void *buffer, size_t size, size_t count, FILE *stream );第一個(gè)參數(shù)buffer指向用戶定義的緩沖區(qū)的指針,用來接收數(shù)據(jù)。第二個(gè)參數(shù)size和第三個(gè)參數(shù)count的用法同fwrite函數(shù)雷同。第四個(gè)參數(shù)stream指向FILE文件類型的指針。圖8-05添加菜單“利用C+函數(shù)”8.3.3 利用C+函數(shù)來實(shí)現(xiàn)在工程EX08_01的基礎(chǔ)上再添加一個(gè)級聯(lián)菜單名為“利用C+函數(shù)”,然后給這個(gè)級聯(lián)菜單添加同樣的兩個(gè)菜單項(xiàng)分別為“寫入數(shù)據(jù)”和“讀取數(shù)據(jù)”,ID分別為ID_FILEIO_CPLUS_WRITE和ID_FILEIO_CPLUS_READ。結(jié)果如圖8-05所示。在CEX08_01View類中再添加上述兩個(gè)菜單項(xiàng)的COMMAND命令消息處理函數(shù),分別為OnFileioCplusRead和OnFileioCplusWrite,并添加如下代碼:1. 在消息處理函數(shù)OnFileioCplusWrite中寫入數(shù)據(jù)void CEX08_01View:OnFileioCplusWrite() / TODO: Add your command handler code hereofstream ofs(c:example3.txt);char szchar20=利用C+函數(shù)來實(shí)現(xiàn);ofs.write(szchar,strlen(szchar);MessageBox(數(shù)據(jù)已寫入);ofs.close();其中ofstream表示輸出流對象,它的成員函數(shù)write用于向文件寫入數(shù)據(jù)。2. 在消息處理函數(shù)OnFileioCplusRead中讀取數(shù)據(jù)void CEX08_01View:OnFileioCplusRead() / TODO: Add your command handler code hereifstream ifs(c:example3.txt);char szchar20;memset(szchar,0,20);ifs.read(szchar,20);MessageBox(szchar);ifs.close();其中ifstream表示輸入流對象,它的成員函數(shù)read用于從文件讀取數(shù)據(jù)。利用C+函數(shù)來實(shí)現(xiàn)文件操作,可以選擇操作的文件是文本文件還是二進(jìn)制文件。文本文件和二進(jìn)制文件的區(qū)別:文件文件是一種特殊的二進(jìn)制文件,當(dāng)它遇到回車鍵10時(shí),寫入文件時(shí)會自動地在它的前面加一個(gè)13,而讀出文件時(shí)遇到1310的組合時(shí),又把它還原到10。而二進(jìn)制文件就是把數(shù)據(jù)原封不動的寫入文件,再原封不動的讀取出來,沒有文本文件的這種轉(zhuǎn)換操作。下面的代碼演示了之間的這種區(qū)別:寫入文件時(shí),按如下填寫:ofstream f(c:1.txt);char buf3;buf0=a;buf1=n;buf2=b;f.write(buf,3);讀出文件時(shí):ifstream f(c:1.txt);f.setmode(filebuf:binary);char buf5;memset(buf,0,5);f.read(buf,5);CString str;str.Format(%d,%d,%d,%d,buf0,buf1,buf2,buf3);MessageBox(str);在寫入文件時(shí)不指定格式,文件將按默認(rèn)的文本格式存儲,然后讀出文件時(shí)指定二進(jìn)制格式,讀出的數(shù)據(jù)如圖8-06所示:圖8-06如果注釋掉f.setmode(filebuf:binary);語句,文件將按文本文件讀出,如圖8-06所示:圖8-07請讀者敲入以上代碼體會一下文本文件和二進(jìn)制文件的不同。8.3.4 利用API函數(shù)來實(shí)現(xiàn)在工程EX08_01的基礎(chǔ)上再添加一個(gè)級聯(lián)菜單名為“利用API函數(shù)”,然后給這個(gè)級聯(lián)菜單添加同樣的兩個(gè)菜單項(xiàng)分別為“寫入數(shù)據(jù)”和“讀取數(shù)據(jù)”,ID分別為ID_FILEIO_API_WRITE和ID_FILEIO_API_READ。結(jié)果如圖8-08所示。圖8-08添加菜單“利用API函數(shù)”在CEX08_01View類中再添加上述兩個(gè)菜單項(xiàng)的COMMAND命令消息處理函數(shù),分別為OnFileioApiRead和OnFileioApiWrite,并添加如下代碼:1. 在消息處理函數(shù)OnFileioApiWrite中寫入數(shù)據(jù):void CEX08_01View:OnFileioApiWrite() / TODO: Add your command handler code hereHANDLE handle;char szchar20=利用API函數(shù)來實(shí)現(xiàn);DWORD dwWrite;handle=CreateFile(c:example4.txt,GENERIC_WRITE,0,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);WriteFile(handle,szchar,strlen(szchar),&dwWrite,NULL);CloseHandle(handle);MessageBox(數(shù)據(jù)已寫入);其中CreateFile函數(shù)在這里表示創(chuàng)建并打開文件,但是我們不能光從字面來理解這個(gè)函數(shù),因?yàn)樗€可以創(chuàng)建及打開
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 遂寧職業(yè)學(xué)院《生物信息與智能醫(yī)學(xué)導(dǎo)論》2023-2024學(xué)年第二學(xué)期期末試卷
- 中級宏觀經(jīng)濟(jì)學(xué)(雙語)(山東聯(lián)盟)知到課后答案智慧樹章節(jié)測試答案2025年春山東財(cái)經(jīng)大學(xué)
- 中式面點(diǎn)制作工藝知到課后答案智慧樹章節(jié)測試答案2025年春青島酒店管理職業(yè)技術(shù)學(xué)院
- 中外兒童文學(xué)經(jīng)典閱讀與寫作知到課后答案智慧樹章節(jié)測試答案2025年春湖南師范大學(xué)
- 中醫(yī)藥基礎(chǔ)入門知到課后答案智慧樹章節(jié)測試答案2025年春內(nèi)江師范學(xué)院
- 天津工藝美術(shù)職業(yè)學(xué)院《影視節(jié)目錄制與傳播》2023-2024學(xué)年第二學(xué)期期末試卷
- 池州學(xué)院《電氣控制與可編程控制技術(shù)》2023-2024學(xué)年第二學(xué)期期末試卷
- 中國音樂學(xué)院《修建性詳細(xì)規(guī)劃制圖》2023-2024學(xué)年第二學(xué)期期末試卷
- 鄭州汽車工程職業(yè)學(xué)院《康復(fù)醫(yī)學(xué)概論》2023-2024學(xué)年第二學(xué)期期末試卷
- 上海電機(jī)學(xué)院《大學(xué)生就業(yè)與創(chuàng)業(yè)指導(dǎo)》2023-2024學(xué)年第一學(xué)期期末試卷
- 華能集團(tuán)企業(yè)文化手冊
- 通信網(wǎng)絡(luò)設(shè)備管理
- 工廠消防安全知識培訓(xùn)課件
- 初中九年級化學(xué)課件化學(xué)反應(yīng)后溶質(zhì)成分的探究
- GB/T 15558.3-2023燃?xì)庥寐竦鼐垡蚁?PE)管道系統(tǒng)第3部分:管件
- 神經(jīng)病學(xué)課件:神經(jīng)病學(xué)總論-
- 物料變更通知單
- PI形式發(fā)票范文模板
- 2023光伏電站無人機(jī)智能巡檢技術(shù)標(biāo)準(zhǔn)
- 室外消防鋼絲網(wǎng)骨架塑料復(fù)合PE管施工方案-2
- 執(zhí)業(yè)醫(yī)師注冊、變更申請表
評論
0/150
提交評論