




已閱讀5頁,還剩24頁未讀, 繼續(xù)免費閱讀
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
第8章 文檔序列化大多數(shù)應用程序都為用戶提供了數(shù)據(jù)的保存功能,這些數(shù)據(jù)可能是電子表格、字處理文檔、一組數(shù)據(jù)或圖形等等。從磁盤存儲器上存取這些數(shù)據(jù)的工作往往是通過文件操作或者數(shù)據(jù)庫操作來完成的。關(guān)于數(shù)據(jù)庫操作的內(nèi)容,我們將在后面的章節(jié)中進行詳細的介紹,在本章的內(nèi)容中,我們主要討論如何通過文件操作實現(xiàn)一般意義上的數(shù)據(jù)存取工作。通過文件操作來實現(xiàn)數(shù)據(jù)的存取工作通常有兩種工作方式:一種是使對象具有序列化;另一種方法就是直接使用CFile對象處理文件。本章就將對這兩部分內(nèi)容分別做出詳解。8.1 序列化在MFC當中,對象的序列化功能主要是通過文檔/視圖結(jié)構(gòu)中特有的文檔對象的序列化機制來實現(xiàn)的。本節(jié),我們將詳細介紹如何使用序列化機制來實現(xiàn)對象的序列化。序列化,簡單地說就是向一個持久性的存儲媒體如磁盤文件保存對象或讀取對象的過程。序列化分為兩部分,當把應用程序數(shù)據(jù)以文件形式存儲在系統(tǒng)磁盤中時,叫做序列化;當從磁盤文件中恢復應用程序數(shù)據(jù)的狀態(tài)時,叫做反序列化,這兩個部分的組合構(gòu)成了Visual C+中的應用程序?qū)ο蟮男蛄谢?.1.1 CArchive類和Serialize函數(shù)Visual C+應用程序中的序列化是通過CArchive類來實現(xiàn)的。CArchive類總是與一個CFile對象相關(guān)聯(lián),CArchive類是作為CFile對象的輸入輸出流而設(shè)計的,如圖8-00所示,它使用經(jīng)過重載的C+流入()操作符從存儲應用程序數(shù)據(jù)的文件中實現(xiàn)讀取和寫入數(shù)據(jù),而將這些數(shù)據(jù)保存到磁盤文件中的工作由CArchive對象指示CFile對象來完成。應用程序框架應用程序?qū)ο?序列化函數(shù)CFile CArchive類圖8-00 CArchive類和CFile類可以實現(xiàn)序列化的類即從CObject繼承而來的類,有一個叫做Serialize的成員函數(shù),序列化工作主要是在這個函數(shù)當中進行的。當應用程序讀取或?qū)懭胛募r,文檔對象的Serialize函數(shù)被調(diào)用,并傳遞用于從文件讀取或向文件寫入數(shù)據(jù)的CArchive對象。在Serialize函數(shù)中,要遵循的典型邏輯是通過調(diào)用CArchive類的IsStoring或IsLoading函數(shù)來判定當前行為是正在對文件寫入還是讀取。根據(jù)這兩個函數(shù)中任何一個的返回值即可判定應用程序需要從CArchive類的I/O流中讀取還是向其寫入。當用戶在打開或保存擁有文檔對象數(shù)據(jù)的文件或者使用文檔對象的Open、Save、Save As菜單命令時,MFC便會自動調(diào)用Serialize函數(shù),一個典型的Serialize函數(shù)如下所示:void CAge:Serialize( CArchive& ar ) CObject:Serialize( ar ); if( ar.IsStoring() ) ar m_years; 其中,ar是一個指明應用程序序列化對象的CArchive引用參數(shù)。CArchive:Serialize成員函數(shù)可以告訴用戶序列化對象當前是否用來存儲或加載??梢詫erialize函數(shù)放置在所創(chuàng)建的任何類中,以便文檔的Serialize函數(shù)中調(diào)用這些類的Serialize函數(shù)。8.1.2 使自己的類支持序列化在前幾章講過的例子中使用CString類的字符串來保存文本行,由于它是MFC類,因此可以串行化自己,將自己寫入磁盤或從磁盤文件中讀取二進制數(shù)據(jù)來建立對象。那么,如果不是標準的MFC類,比如用戶自己定義的類,如何讓它支持序列化呢?要讓用戶定義的類支持序列化,一般分為五步:1.從CObject或其派生類派生出用戶的類2.在類聲明文件中,加入DECLARE_SERIAL宏。編譯時,編譯器將擴充該宏,這是串行化對象所必需的。3.重載Serialize()成員函數(shù),加入必要的代碼,用以保存對象的數(shù)據(jù)成員到CArchive對象以及從CArchive對象載入對象的數(shù)據(jù)成員狀態(tài)。4.定義一個不帶參數(shù)的構(gòu)造函數(shù)。5.在實現(xiàn)文件中加入IMPLEMENT_SERIAL宏。下面將通過一個實例來演示如何讓用戶定義的類支持序列化功能。8.1.3 實例:保存和顯示圖形還記得第6章的繪圖程序嗎,用戶畫好的圖形不僅不能保存下來,而且當窗口發(fā)生重繪時,圖形也就不見了,本實例就將解決這兩個問題,不僅使所畫的圖形在窗口重繪時依然保留,而且還給它添加了保存及再顯示功能。我們在第6章繪圖程序上加的內(nèi)容夠多了,這里為了更清晰的講述本章的重點序列化,將新建一個工程,當然,這個工程所要實現(xiàn)的功能還是和第6章繪圖程序一樣,只不過給它加個序列化,完整例程請參見光盤中的例子代碼EX08_00,具體操作步驟如下:l 步驟1:新建一個MFC單文檔應用程序,工程名為EX08_00或用戶自定義。l 步驟2:為新建的工程先實現(xiàn)第6章的簡單繪圖功能(詳細步驟請參見第6章)。1. 在資源面板中修改原來的標準菜單,新插入一個菜單名為“繪圖”,下面有四個菜單項“點”、“線”、“矩形”、“橢圓”,修改它們的ID分別為:ID_GRAPH_DOT、ID_GRAPH_LINE、ID_GRAPH_RECTANGLE、ID_GRAPH_ELLIPSE。2. 在CEX08_00View類中添加兩個成員變量CPoint m_ptOrigin和int m_nType,分別表示繪圖的起點和繪圖的類型,并在構(gòu)造函數(shù)中初始化為0和-1。3. 在CEX08_00View中加入四個菜單項“點”、“線”、“矩形”和“橢圓”的WM_COMMAND消息的響應函數(shù)OnGraphDot、OnGraphLine、OnGraphRectangle 、OnGraphEllipse,在消息響應函數(shù)中設(shè)置變量m_nType的值。m_nType為0,表示畫點;m_nType為1,表示畫線;m_nType為2,表示畫矩形;m_nType為3,表示畫橢圓。4. 在CEX08_00View類中加入WM_LBUTTONDOWN和WM_LBUTTONUP的消息響應函數(shù)OnLButtonDown和OnLButtonUp。在OnLButtonDown中保存鼠標按下的點;在OnLButtonUp中,根據(jù)m_nType的值畫相應的圖形。l 步驟3:給工程添加一個可序列化的類CGraph。1 新建一個類CGraph,從CObject派生。打開工作臺ClassView頁面,鼠標右擊最頂層的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當單擊OK按鈕添加該類時,會彈出一個如圖8-02所示的對話框,該對話框提示Class Wizard無法為從CObject派生出的CGraph類找到合適的頭文件。我們不用理會它,在此消息框中單擊確定按鈕即可,因為合適的頭文件已經(jīng)包含在CGraph類中。2 在CGraph類中重載Serialize()成員函數(shù)。我們要實現(xiàn)序列化,先對其進行改造,在工作臺的ClassView頁面中選擇CGraph類,單擊鼠標右鍵,選擇Add Member Function增加一個成員函數(shù),在彈出的對話框中Functiong Type中輸入void,在Function Declaretion編輯框中輸入 Serialize(CArchive& ar),然后選擇Public,按OK即可。然后在ClassView中可以看到這個函數(shù)。圖8-02 創(chuàng)建新類時的警告信息3. 在類CGraph的頭文件中,加入DECLARE_SERIAL宏,代碼如下:class CGraph : public CObject public:void Serialize(CArchive& ar);CGraph();virtual CGraph();DECLARE_SERIAL(CGraph);要對CGraph類實現(xiàn)序列化,需要在類的.h文件中加入宏DECLARE_SERIAL的調(diào)用,這個宏不需要加分號,并且后面有一個參數(shù)表示添加序列化特性的類名。4 定義一個不帶參數(shù)的構(gòu)造函數(shù)。打開工作臺的CGraph類,可以看到,不帶參數(shù)的構(gòu)造函數(shù)已經(jīng)存在于類中了,這是我們最開始創(chuàng)建CGraph這個新類時自動添加的。MFC在從磁盤文件載入對象狀態(tài)并重建對象時,需要有一個缺省的不帶任何參數(shù)的構(gòu)造函數(shù)。序列化對象將用該構(gòu)造函數(shù)生成一個對象,然后調(diào)用Serialize()函數(shù),用重建對象所需的值來填充對象的所有數(shù)據(jù)成員變量。5 在類CGraph實現(xiàn)文件中加入IMPLEMENT_SERIAL宏。打開類CGraph的實現(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宏用于定義一個從CObject派生的可序列化類的各種函數(shù)。宏的第一和第二個參數(shù)分別代表可序列化的類名和該類的直接基類。第三個參數(shù)是對象的版本號,它是一個大于或等于零的整數(shù)。MFC序列化代碼在將對象讀入內(nèi)存時檢查版本號。如果磁盤文件上的對象的版本號和內(nèi)存中的對象的版本號不一致,MFC將拋出一個CArchiveException異常,阻止程序讀入一個不匹配版本的對象。現(xiàn)在,我們就可以象使用標準MFC類一樣使用CGraph的序列化功能了。l 步驟4:構(gòu)造CGraph類,做準備工作。1. 在類CGraph的頭文件中添加三個成員變量m_ptOrigin、m_ptEnd、m_nType。我們既然想保存下來所畫的圖形,那么至少要保留住關(guān)于這些圖形的一些信息,不管用戶畫的是線、矩形還是橢圓,它們都有一個共同點:就是由兩點決定這個圖形,那么從起點到終點畫的到底是什么圖形,就要看m_nType的值了,因此這里定義了兩個CPoint型的變量,用于保存用戶所畫的一組圖形的各個起點和終點;另一個為int型變量用來指定所畫的每個圖形的類型。class CGraph : public CObject public:CPoint m_ptOrigin; /記錄起始點CPoint m_ptEnd; /記錄終點int m_nType; /記錄畫圖類型void Serialize(CArchive& ar);CGraph();virtual CGraph();DECLARE_SERIAL(CGraph);2. 給類CGraph添加一個帶參數(shù)的構(gòu)造函數(shù)鼠標右擊CGraph類,在彈出的快捷方式菜單中選擇Add Member Function,函數(shù)類型(Function Type)編輯框中什么都不填,因為構(gòu)造函數(shù)沒有返回值,函數(shù)聲明(Function Declaration)為CGraph(int m_drawType,CPoint m_ptFrom, CPoint m_ptTo),編輯這個帶參數(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òu)造函數(shù)中,用傳遞到構(gòu)造函數(shù)的畫圖類型、起點和終點三個參數(shù)來初始化類CGraph中相應意義的三個變量。l 步驟5:在文檔類CEX08_00Doc中定義一個成員變量,用于保存每一個圖形對象?,F(xiàn)在已經(jīng)有了一個可以用來表示用戶所繪圖形的對象,那么接下來的重點是如何將這些對象保存下來。當用戶畫一個圖形,就產(chǎn)生一個這樣的圖形對象,因此,這個對象是動態(tài)的不斷增長的。CObArray類是一個對象數(shù)組類,它可以動態(tài)調(diào)整自己的大小以適應放在它里面的元素的個數(shù)。它可以存放任何從CObject類派生出的對象(如前面的CGraph對象),它的大小只受系統(tǒng)的內(nèi)存空間的限制。MFC中其他動態(tài)數(shù)組類包括CStringArray、CByteArray、CWordArray、CDWordArray、CPtrArray,它們的不同之處在于存放的對象的類型。這里,我們鼠標右擊CEX08_00Doc類,在彈出的快捷方式菜單中選擇Add Member Variable,變量類型為CObArray,變量名為m_obArray。l 步驟6:將用戶畫的每一個圖形對象保存到m_obArray中。要將圖形對象保存到對象數(shù)組m_obArray中,首先就要得到對象的繪圖類型和起點、終點,然后創(chuàng)建一個新的圖形對象,并把它加入到對象數(shù)組m_obArray中。我們知道,當用戶按下鼠標左鍵,然后隨之拖動出一個圖形,最后當鼠標抬起的時候,那么就是這個圖形對象生成的時候,因此,應該在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);這段代碼中,首先聲明一個CGraph類的一個指針對象,并且用該類的帶參數(shù)的構(gòu)造函數(shù)來構(gòu)建這個對象,構(gòu)造函數(shù)中的參數(shù)就是OnLButtonUp函數(shù)中用于畫圖的類型和起點、終點。用戶再回頭看一看CGraph類中帶參數(shù)的構(gòu)造函數(shù),就會明白,實際上構(gòu)造函數(shù)傳遞過來的參數(shù)是為了初始化CGraph類的三個成員變量m_ptOrigin、m_ptEnd、m_nType,這三個成員變量構(gòu)成了一個CGraph類對象,然后將該對象通過m_obArray.Add保存到對象數(shù)組中。由于對象數(shù)組是在文檔類中定義的,在視圖類中不能直接引用,因此,前邊需要調(diào)用GetDocument函數(shù)來取得訪問文檔類的權(quán)利。最后別忘了在視圖類的實現(xiàn)文件中添加#include Graph.h,將類CGraph的頭文件包含進來。l 步驟7:在CGraph類中完成繪圖功能。CGraph類對象中包含著三個重要的畫圖參數(shù),因此,這個對象是可以繪制自身的,當視圖類需要重繪圖形時,它只需要向該類發(fā)送一條消息,告訴它要繪制自己就可以了。鼠標右擊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ù)中用到三個繪圖參數(shù)就是CGraph類的三個成員變量。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);在這個函數(shù)中,首先判斷數(shù)組對象中是否有元素,即是否有圖形需要繪制,如果沒有就什么都不做,如果有元素,那么就通過for循環(huán)從文檔類的數(shù)組對象中依次取出圖形對象(包括三個重要繪圖參數(shù)),然后調(diào)用圖形對象的Draw函數(shù),即第7步驟中的Draw函數(shù)。到現(xiàn)在,這個繪圖程序就具備了重繪的功能,無論窗口怎樣改變,用戶所繪制的圖形依然顯示在窗口上。下面我們繼續(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)用每個對象的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,這個符號及可以連用,亦可以分開來用,如ar m_nType;ar m_ptOrigin;同樣arm_nTypem_ptOriginm_ptEnd;中的是把數(shù)據(jù)存入ar中。需要注意的是,三個變量讀取和保存的順序一定要和在類CGraph中帶參數(shù)的構(gòu)造函數(shù)中三個參數(shù)的聲明順序是一致的?,F(xiàn)在編譯運行這個程序,隨意畫幾個圖形,然后選擇菜單“文件”|“保存”,在彈出的“保存”對話框中輸入文件名將所畫圖形保存到文件中,然后關(guān)閉應用程序,再重新運行該應用程序,選擇 “文件”|“打開” 菜單,在彈出的“打開”對話框上雙擊先前保存有圖形的文件,就可以看到先前保存的圖形再一次顯示在窗口上了。8.1.4 刪除文檔數(shù)據(jù)當用戶單擊“打開”和“新建”菜單項或按鈕時會自動調(diào)用DeleteContents虛函數(shù),這個時候是刪除文檔數(shù)據(jù)的最好時機。下面將以上一小節(jié)的例子EX08_00工程為基礎(chǔ),講述如何刪除文檔數(shù)據(jù)。打開工作臺的Class View頁面,鼠標右擊CEX08_00Doc類,在彈出的快捷方式菜單中選擇Add Virtual Function,在彈出的對話框上左邊的列表框中找到DeleteContents函數(shù),雙擊該函數(shù)將其加入到文檔類中,用戶可以在這個函數(shù)中刪除文檔數(shù)據(jù)。通常,用戶在刪除文檔數(shù)據(jù)時,經(jīng)常犯以下兩種錯誤:1錯誤方法一: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錯誤方法二:int index= m_obArray.GetSize();for (int i=0;iindex;i+)delete (CGraph *) m_obArray.GetAt(i); m_obArray.RemoveAt(i);原因:每刪除一個數(shù)組元素,數(shù)組都會重新排序,它的下標會變。正確的刪除方法應按如下方式: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實現(xiàn)文件的前面添加#include Graph.h語句,將類CGraph的頭文件包含進來。序列化簡化了對象的保存和載入,為對象提供了持久性。但是,序列化本身仍具有一定的局限性。由于序列化一次從文件中載入所有對象,因此,它不適合于大文件編輯器和數(shù)據(jù)庫。對于數(shù)據(jù)庫和大文件編輯器,它們每次只是從文件中讀入一部分。此時,就不應該采用文檔的序列化機制來直接讀取和保存文件了。另外,使用外部文件格式(預先定義的文件格式而不是本應用程序定義的文件格式)的程序一般也不使用文檔的序列化。8.2 CFile類文件的處理包括創(chuàng)建及打開文件、數(shù)據(jù)讀取、關(guān)閉文件等一系列操作。在MFC中,包含文件相關(guān)操作的基類為CFile類。CFile類是從CObject類派生而來的,CFile類用來處理正常文件的I/O操作,它直接供無緩沖的、二進制磁盤輸入/輸出服務,并且通過其派生類間接支持文本文件和內(nèi)存文件。8.2.1 CFile類的構(gòu)造函數(shù)CFile類有三個構(gòu)造函數(shù),其原型如下:1. CFile();2. CFile(int nFile);3. CFile( LPCTSTR lpszFileName, UINT nOpenFlags );第一種方式,在聲明CFile實例后,不需要設(shè)定任何參數(shù)。第二種方式,有一個參數(shù)nFile為一個已打開文件的句柄,就是建立已打開文件的另一個實例,它不做任何錯誤檢查,直接用構(gòu)造函數(shù)打開。第三種方式,參數(shù)lpszFileName指定想要操作的文件的名稱和路徑的字符串,路徑可以是相對的或絕對的;參數(shù)nOpenFlags指定文件的共享和存取方式,該參數(shù)可以用OR來組合下面所列的值:l CFile:modeCreate指示構(gòu)造函數(shù)創(chuàng)建一個新文件,如果該文件已存在,則該文件截短為0l CFile:modeRead 打開文件只用于讀l CFile:modeReadWrite 打開文件用于讀寫l CFile:modeWrite 打開文件只用于寫l CFile:modeNoInberit 阻禁止該文件被子進程繼承l(wèi) CFile:shareDenyNone 打開文件,不允許其它進程讀或?qū)懺撐募?。如果該文件已由其它任何進程用兼容方式打開,則Create將失敗l CFile:shareDenyWrite 打開文件,不允許其它進程寫該文件。如果該文件已由其它任何進程用兼容方式或?qū)懛绞酱蜷_,則Create將失敗l CFile:shareDenyRead 打開文件,不允許其它進程讀該文件。如果該文件已由其它任何進程用兼容方式或讀方式打開,則Create將失敗l CFile:shareExclusive 以獨占方式打開文件,不允許其它進程寫該文件。如果該文件已用其它讀或?qū)懛绞酱蜷_,即使是當前進程打開,則構(gòu)造也將失敗l CFile:shareCompat 以兼容方式打開文件,允許給定機器上任何進程打開該文件任意次。如果該文件已用其它任何共享方式打開,構(gòu)造將失敗l CFile:typeText 設(shè)置文本方式,對回車換行進行特殊處理,它只用于派生類l CFile:typeBinary 設(shè)置二進制方式,它只用于派生類以上描述了文件共享和存取標志。在第三種構(gòu)造函數(shù)中,一個存取許可和一個共享選項是必需的。modeCreate 和modeNoInberit方式是可選的。8.2.2 打開文件CFile類用Open函數(shù)來創(chuàng)建和打開文件,用Open創(chuàng)建新文件,必須有一個文件名,并且選擇一定的打開方式,函數(shù)原形如下:virtual BOOL Open( LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError = NULL);第一個參數(shù)lpszFileName指定想要操作的文件的名稱和路徑的字符串。第二個參數(shù)nOpenFlags指定文件的共享和存取方式,用法同CFile類第三種構(gòu)造函數(shù)中的nOpenFlags參數(shù)用法相同。第三個參數(shù)pError,表示文件異常處理對象的指針,當文件打開失敗時,系統(tǒng)會將文件異常消息存放在CFileException打開文件的m_cause數(shù)據(jù)成員。用戶可以根據(jù)調(diào)用Open函數(shù)后的返回值來簡單的判斷文件是否正常打開或創(chuàng)建。若用戶想了解是什么原因造成打開文件失敗時,就必須指定CFileException對象,該對象專門處理文件操作上的問題。在CFileException對象內(nèi)的m_cause數(shù)據(jù)成員便負責記錄文件操作到底出了哪些問題。8.2.3 讀寫數(shù)據(jù)文件創(chuàng)建及打開后,就可以對數(shù)據(jù)進行讀取或?qū)懭肓?。CFile對磁盤文件的定點、讀取和寫入操作分別通過函數(shù)Write,Read和Seek進行的。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é)偏移。第一個參數(shù)lOff,表示指針偏移的字節(jié)數(shù)。第二個參數(shù)nFrom,表示偏移的起點,該參數(shù)必須是下列值中的唯一:l CFile:begin表示從文件的頭部開始偏移指針l CFile:current表示從文件的當前位置開始偏移指針l CFile:end表示從文件的尾部開始偏移指針8.2.4 關(guān)閉文件文件的打開和關(guān)閉是相對的,打開一個文件之后,必須把它關(guān)閉,關(guān)閉文件可以調(diào)用Close函數(shù),原形如下:virtual void Close( );8.3 文件I/O處理實現(xiàn)文件操作的方法有很多,用戶不僅可以通過先前學過的CFile類來進行文件操作,還可以通過C或C+函數(shù)、API函數(shù)來實現(xiàn),更可以通過創(chuàng)建打開和保存對話框來實現(xiàn)文件操作。本章將分別介紹這幾種文件操作的方法及如何創(chuàng)建打開和保存對話框。8.3.1 利用MFC類來實現(xiàn)這里將通過一個例子來介紹如何通過定義類CFile對象來實現(xiàn)文件的I/O操作,完整例程請參見光盤中例子代碼EX08_01,具體操作步驟如下:l 步驟1:新建一個MFC單文檔應用程序,工程名為EX08_01或用戶自定義。l 步驟2:給工程新添加一個菜單,名為“文件操作”,在下面再添加一個級聯(lián)菜單名為“利用MFC類”,然后給這個級聯(lián)菜單添加兩個菜單項分別為“寫入數(shù)據(jù)”和“讀取數(shù)據(jù)”,ID分別為ID_FILEIO_MFC_WRITE和ID_FILEIO_MFC_READ。結(jié)果如圖8-03所示。圖8-03 添加菜單“利用MFC類”l 步驟3:在CEX08_01sView類中添加上述兩個菜單項的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類來實現(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 );第一個參數(shù)dest表示要被填充的緩沖區(qū)。第二個參數(shù)c表示要填充的字符。第三個參數(shù)count,填充的長度。本例中用0字符將整個緩沖區(qū)填滿的用意是防止在讀取文件數(shù)據(jù)時在正常數(shù)據(jù)的后面出現(xiàn)亂碼。因為,我們并不知道將要讀取多少個字符,因此,只好將緩沖區(qū)中的所有字符讀出來,而這個緩沖區(qū)中沒有被寫入數(shù)據(jù)的那部分內(nèi)存中的內(nèi)容是不定的,讀出來會是亂碼,因此,在往這個緩沖區(qū)讀入數(shù)據(jù)之前,先將它清0,用0字符填充的原因很簡單,在C語言中,一個字符串的結(jié)束標志就是0字符。8.3.2 利用C函數(shù)來實現(xiàn)在工程EX08_01的基礎(chǔ)上再添加一個級聯(lián)菜單名為“利用C函數(shù)”,然后給這個級聯(lián)菜單添加同樣的兩個菜單項分別為“寫入數(shù)據(jù)”和“讀取數(shù)據(jù)”,ID分別為ID_FILEIO_C_WRITE和ID_FILEIO_C_READ。結(jié)果如圖8-04所示。圖8-04添加菜單“利用C函數(shù)”在CEX08_01View類中添加上述兩個菜單項的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ù)來實現(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ù)返回一個指向FILE文件類型的指針,該函數(shù)有兩個參數(shù),第一個參數(shù)filename表示要打開的文件名;第二個參數(shù)mode表示打開方式,打開方式有如下幾個選擇:r 打開文件用于讀取數(shù)據(jù)w 打開文件用于寫入數(shù)據(jù)a打開文件用于在文件尾部寫數(shù)據(jù),并不覆蓋前面的數(shù)據(jù)r+打開文件即可以讀又可以寫,前提是該文件必須存在w+ 打開一個空文件即可以讀又可以寫,如果文件存在,則文件的內(nèi)容將被清除a+打開文件可以讀取并且在尾部可以添加數(shù)據(jù)函數(shù)fwrite用于向文件中寫入數(shù)據(jù),原形如下:size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );第一個參數(shù)buffer是用戶定義的緩沖區(qū)的指針,該參數(shù)中包含用戶將要寫到文件中的數(shù)據(jù);第二個參數(shù)size和第三個參數(shù)count是相輔相成的,代碼中的下面語句fwrite(szchar,1,strlen(szchar),pFile);也可以寫成如下形式;fwrite(szchar,strlen(szchar),1,pFile);假設(shè)用戶定義的緩沖區(qū)大小為20個字節(jié),那么如果按照第一種代碼形式,表示需要一個緩沖區(qū)(size為1),緩沖區(qū)大小為20個字節(jié)(count為20);如果按照第二種代碼形式,表示需要20個緩沖區(qū)(size為20),每個緩沖區(qū)大小為1個字節(jié)(count為1)。第四個參數(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 );第一個參數(shù)buffer指向用戶定義的緩沖區(qū)的指針,用來接收數(shù)據(jù)。第二個參數(shù)size和第三個參數(shù)count的用法同fwrite函數(shù)雷同。第四個參數(shù)stream指向FILE文件類型的指針。圖8-05添加菜單“利用C+函數(shù)”8.3.3 利用C+函數(shù)來實現(xiàn)在工程EX08_01的基礎(chǔ)上再添加一個級聯(lián)菜單名為“利用C+函數(shù)”,然后給這個級聯(lián)菜單添加同樣的兩個菜單項分別為“寫入數(shù)據(jù)”和“讀取數(shù)據(jù)”,ID分別為ID_FILEIO_CPLUS_WRITE和ID_FILEIO_CPLUS_READ。結(jié)果如圖8-05所示。在CEX08_01View類中再添加上述兩個菜單項的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ù)來實現(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ù)來實現(xiàn)文件操作,可以選擇操作的文件是文本文件還是二進制文件。文本文件和二進制文件的區(qū)別:文件文件是一種特殊的二進制文件,當它遇到回車鍵10時,寫入文件時會自動地在它的前面加一個13,而讀出文件時遇到1310的組合時,又把它還原到10。而二進制文件就是把數(shù)據(jù)原封不動的寫入文件,再原封不動的讀取出來,沒有文本文件的這種轉(zhuǎn)換操作。下面的代碼演示了之間的這種區(qū)別:寫入文件時,按如下填寫:ofstream f(c:1.txt);char buf3;buf0=a;buf1=n;buf2=b;f.write(buf,3);讀出文件時: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ù)據(jù)如圖8-06所示:圖8-06如果注釋掉f.setmode(filebuf:binary);語句,文件將按文本文件讀出,如圖8-06所示:圖8-07請讀者敲入以上代碼體會一下文本文件和二進制文件的不同。8.3.4 利用API函數(shù)來實現(xiàn)在工程EX08_01的基礎(chǔ)上再添加一個級聯(lián)菜單名為“利用API函數(shù)”,然后給這個級聯(lián)菜單添加同樣的兩個菜單項分別為“寫入數(shù)據(jù)”和“讀取數(shù)據(jù)”,ID分別為ID_FILEIO_API_WRITE和ID_FILEIO_API_READ。結(jié)果如圖8-08所示。圖8-08添加菜單“利用API函數(shù)”在CEX08_01View類中再添加上述兩個菜單項的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ù)來實現(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)建并打開文件,但是我們不能光從字面來理解這個函數(shù),因為它還可以創(chuàng)建及打開
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 童年的回憶關(guān)于童年的故事記事10篇
- 工作假期旅行授權(quán)證明書(6篇)
- 真實有效的在職任職情況證明(7篇)
- 讀后感我最喜歡的一本書(14篇)
- 2025年文旅地產(chǎn)項目開發(fā)規(guī)劃與融合發(fā)展前景研究報告
- 農(nóng)業(yè)機械化智能化2025年農(nóng)業(yè)資源環(huán)境承載力與可持續(xù)發(fā)展戰(zhàn)略研究報告
- 2025年工業(yè)互聯(lián)網(wǎng)平臺安全多方計算技術(shù)在工業(yè)互聯(lián)網(wǎng)安全架構(gòu)中的應用報告001
- 文化產(chǎn)業(yè)園區(qū)的產(chǎn)業(yè)集聚與服務體系建設(shè)與實施路徑報告
- 2025屆安徽省滁州市高三一模考試地理試題(解析版)
- 2025年房地產(chǎn)市場區(qū)域分化與投資策略的智慧城市建設(shè)研究報告
- 2023年10月自考00012英語(一)真題及答案含評分標準
- 混凝土配合比自動計算書
- 過敏性休克搶救步驟流程圖
- 華南理工大學2019級大學物理(I)期末試卷A卷及答案
- 國開學習網(wǎng)《小學語文教學研究》形考任務1-5答案
- 骨代謝標志物在骨質(zhì)疏松診療中的應用指南
- 電氣控制及Plc應用技術(shù)電子教案
- 部編版四季之美課件完美版公開課一等獎課件省課獲獎課件
- 三江學院輔導員考試題庫
- OPL單點課培訓教材-課件
- 《基礎(chǔ)化學》考試復習題庫大全(600多題)
評論
0/150
提交評論