標籤:使用 os io 檔案 資料 ar cti 代碼
一 MFC的檔案操作
1 相關類
CFile類-封裝了檔案控制代碼以及操作檔案的API函數。
CFileFind類-封裝了檔案搜尋功能。
2CFile類的使用
2.1 檔案讀寫
2.1.1 建立或者開啟檔案
CFile::Create
2.1.2 檔案讀寫
CFile::Read/Write
2.1.3 關閉檔案
CFile::Close
注意:1 檔案讀寫需要異常處理
2 注意檔案的指標位置
2.2 檔案屬性的擷取和設定
2.2.1 CFile::GetStatus
2.2.2 CFile::SetStatus
3CFileFind類的使用
3.1 開始尋找(指定尋找的目錄)
CFileFind::FindFile
3.2 尋找下一個(擷取當前檔案資訊,返回下一個檔案是否存在)
CFileFind::FindNextFile
3.3 擷取/判斷檔案資訊
CFileFind::GetXXX/IsXXX
3.4 結束尋找
CFileFind::Close
測試Cfile類編寫測試程式:
建立一個Win32 Console Application 注意選擇MFC庫的支援
編寫如下測試代碼:
/***************************************** 測試CFile*******************************************/void CFileTest (){ // 定義CFile對象CFile file;if (! file.Open ("D:/test.txt", CFile::modeCreate | CFile::modeReadWrite)){// 開啟檔案,如果檔案不存在就建立檔案,並且以讀和寫的許可權開啟檔案AfxMessageBox ("open file failed!");return;}try{// 寫檔案file.Write ("This is a test txt file!", strlen ("This is a test txt file!"));// 讀檔案char szBuf [256] = {0};// 注意:上面寫完檔案後檔案指標位於檔案末尾// 移動檔案指標到檔案頭file.SeekToBegin ();file.Read (szBuf, 256);cout << szBuf << endl;}catch (CFileException* e){// 異常處理...file.Close ();} // 關閉檔案file.Close ();}/************************************************** 測試CFile::GetStatus、CFile::SetStatus修改當前檔案的建立日期***************************************************/void FileStatusTest (){CFileStatus status;// 儲存檔案狀態資訊的結構體CFile::GetStatus ("D:/test.txt", status);CTimeSpan span (7,0, 0, 0);// CTimeSpan 時間間隔類 status.m_ctime -= span;CFile::SetStatus ("D:/test.txt", status);}/***************************************************** 測試CFileFind, 尋找指定路徑下的所有檔案******************************************************/void CFileFindTest (CString strPath){// 尋找strPath路徑下的所有(*.*)檔案strPath += "/*.*";CFileFind find;// 開始尋找BOOL bRet = find.FindFile (strPath);while (bRet){// 擷取當前檔案資訊並尋找下一個檔案bRet = find.FindNextFile ();if (! bRet)return;if (! find.IsDots()){// 點目錄不處理,防止死迴圈if (find.IsDirectory ()){// 目錄則繼續進入尋找// cout 無法輸出CString類型//cout << "[" << find.GetFileName () << "]" << endl;printf ("[%s]\n", find.GetFileName ());CFileFindTest (find.GetFilePath ());}else{// 輸出普通檔案//cout << find.GetFileName() << endl;printf ("%s\n", find.GetFileName ());}}}find.Close ();}int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]){ //CFileTest ();//FileStatusTest ();CFileFindTest ("D:");return 0;}
注意:
檔案同時具備讀寫權限時要寫成:CFile::modeReadWrite而不是分開寫讀和寫入權限。
CtimeSpan 為是時間間隔類,可以用來對現有時間做加減運算
Cout不能輸出Cstring類型,應該使用printf
使用Cfile操作檔案時使用異常處理
序列化
這裡之所以把檔案操作和序列化放在一起,是因為序列化主要是用來方便檔案操作的
1 概念-將資料以二進位流的方式依次寫入到檔案或者從檔案中讀取的過程。
2 相關類
CArchive類-功能是完成具體的資料讀寫。(代替CFile類的Read/Write函數)。
3 使用
3.1 建立或者開啟檔案
CFile::Create
3.2 檔案讀寫
3.2.1 構造CArchive對象
3.2.2 資料讀寫
>> 讀操作
<< 寫操作
3.2.3 關閉CArchive對象
CArchive::Close
3.3 關閉檔案
CFile::Close
建立一個Win32 Console Application, 選擇MFC庫的支援
編寫如下測試代碼:
// 儲存資料的過程(寫操作)void Store(){// 開啟或者建立檔案CFile file;BOOL bRet=file.Open("c:/serial.dat",CFile::modeCreate|CFile::modeWrite);if (!bRet){printf("檔案開啟失敗!");return;}// 構造CArchive對象CArchive ar(&file,CArchive::store);// 寫資料ar<<100<<12.25<<'A';// 關閉CArchive對象ar.Close();// 關閉檔案file.Close();}// 載入資料的過程(讀操作)void Load(){// 開啟檔案CFile file;BOOL bRet=file.Open("c://serial.dat",CFile::modeRead);if (!bRet)return; // 構造CArchive對象CArchive ar(&file,CArchive::load);// 讀資料int iValue=0;double dValue=0.0;char cValue;ar>>iValue>>dValue>>cValue;// 關閉對象ar.Close();// 關閉檔案file.Close();// 輸出printf("iValue=%d\n",iValue);printf("dValue=%f\n",dValue);printf("cValue=%c\n",cValue);}int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]){Store();Load();return 0;}
注意:
在Win C下檔案路徑寫成C:/serial.dat 永遠要比 C:\\serial.dat 要好,一個反斜線“/”不僅寫起來簡單而且具有更好的通用性,不易出錯。
檔案與CArchive的模式要一致,寫<< CArchive::store, 讀 >> CArchive::load
資料讀寫的順序要一致
對象的序列化:
1 概念
序列化對象-將對象的類的資訊以及對象的成員變數以二進位流的方式依次寫入到檔案的過程。
還原序列化對象-從檔案中首先讀取類的資訊建立對象,然後讀取成員變數賦值給建立的對象的過程。
2 定義支援序列化的類
2.1 派生自CObject類
2.2 在類的定義中添加序列化的聲明宏 DECLARE_SERIAL(className)
2.3 在類的實現中添加序列化的實現宏 IMPLEMENT_SERIAL(...)
2.4 重寫CObject::Serialize()函數,在函數中,完成成員變數的序列化。
3 使用
在讀寫對象時,參數是對象的指標。
建立一個Win32 Console Application, 選擇MFC庫的支援
編寫如下測試代碼: 其中定義了一個支援序列化的類CPerson ,且CPerson中包含了一個普通類的成員m_course, 把該成員當普通變數用即可
// 支援序列化的課程類class Course{public:int m_iNO;CString m_strName;};// 定義一個支援序列化的類class CPerson : public CObject{public:void Show (){printf ("Name:%s,Age:%d\niNO:%d, CourseName:%s\n", m_strName, m_iAge, m_course.m_iNO ,m_course.m_strName);}CPerson (){};CPerson (int age, CString strName, int iNO, CString strCourName) : m_iAge (age), m_strName (strName){m_course.m_iNO = iNO;m_course.m_strName = strCourName;}virtual void Serialize (CArchive &ar);private:int m_iAge;CString m_strName;Course m_course; // 序列化聲明宏DECLARE_SERIAL (CPerson)};// 序列化實現宏IMPLEMENT_SERIAL (CPerson, CObject, 1)void CPerson::Serialize (CArchive &ar){// 如果父類成員需要序列化時,首先調用父類相關函數CObject::Serialize (ar);if (ar.IsStoring ())// 寫ar << m_iAge << m_strName << m_course.m_iNO << m_course.m_strName;else// 讀ar >> m_iAge >> m_strName >> m_course.m_iNO >> m_course.m_strName;}// 寫對象void ObjectStore (CPerson* pPerson){CFile file;BOOL bRet = file.Open ("E:/Serial.dat", CFile::modeCreate | CFile::modeWrite); if (! bRet)return;CArchive ar(&file, CArchive::store);ar << pPerson;ar.Close();file.Close ();}// 讀對象void ObjectLoad (){CFile file;BOOL bRet = file.Open ("E:/Serial.dat", CFile::modeRead);if (! bRet)return;CArchive ar (&file, CArchive::load);// 注意這裡是指標,對象不用我們去定義CPerson *pPerson = NULL;ar >> pPerson;if (pPerson){ pPerson->Show ();delete pPerson;pPerson = NULL;}ar.Close();file.Close ();}int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]){CPerson person (24, "關羽", 1, "武術");ObjectStore (&person);ObjectLoad ();return 0;}
實現原理:
先展開兩個宏:
DECLARE_SERIAL(CPerson)
_DECLARE_DYNCREATE(CPerson) AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, CPerson* &pOb);
IMPLEMENT_SERIAL(CPerson,CObject,1)
CObject* PASCAL CPerson::CreateObject() { return new CPerson; } _IMPLEMENT_RUNTIMECLASS(CPerson, CObject, 1, CPerson::CreateObject) AFX_CLASSINIT _init_CPerson(RUNTIME_CLASS(CPerson));CArchive& AFXAPI operator>>(CArchive& ar, CPerson* &pOb){ pOb = (CPerson*) ar.ReadObject(RUNTIME_CLASS(CPerson)); return ar; }
很容易看到序列化的申明和實現宏張開後明顯包含MFC的: 動態建立機制 和 運行時類資訊機制
那麼就不難理解為什麼CArchive 只需要對象指標就可以操作一個對象了(運行式動態建立對象)
首先展開宏後觀察幾個成員的作用:
相關結構體
struct AFX_CLASSINIT{ AFX_CLASSINIT(CRuntimeClass* pNewClass) { AfxClassInit(pNewClass); { pModuleState->m_classList.AddHead(pNewClass); } } };
該結構體就包含一個函數:把運行時類資訊的地址加入到當前程式模組狀態資訊的一個鏈表成員變數m_classList中
operator>>,友元函數,讀對象的函數。設定友元的目的是得到當前類的私人成員。
_init_CPerson,全域的結構體變數。作用是將當前類的運行時類資訊的地址儲存到模組狀態資訊的一個鏈表m_classList中。
寫對象的過程跟蹤: