文件操作的方法
使用Visual C++編程,有如下方法進行文件操作:
(1)使用標准C運行庫函數,包括fopen、fclose、fseek等。
(2)使用Win16下的文件和目錄操作函數,如lopen、lclose、lseek等。不過,在Win32下,這些函數主要是為了和Win16向後兼容。
(3)使用Win32下的文件和目錄操作函數,如CreateFile,CopyFile,DeleteFile,FindNextFile,等等。
Win32下,打開和創建文件都由CreateFile完成,成功的話,得到一個Win32下的句柄,這不同於“C”的fopen返回的句柄。在Win16下,該句柄和C運行庫文件操作函數相容。但在Win32下,“C”的文件操作函數不能使用該句柄,如果需要的話,可以使用函數_open_osfhandle從Win32句柄得到一個“C”文件函數可以使用的文件句柄。
關閉文件使用Win32的CloseHandle。
在Win32下,CreateFile可以操作的對象除了磁盤文件外,還包括設備文件如通訊端口、管道、控制台輸入、郵件槽等等。
(4)使用CFile和其派生類進行文件操作。CFile從CObject派生,其派生類包括操作文本文件的CStdioFile,操作內存文件的CmemFile,等等。
CFile是建立在Win32的文件操作體系的基礎上,它封裝了部分Win32文件操作函數。
最好是使用CFile類(或派生類)的對象來操作文件,必要的話,可以從這些類派生自己的文件操作類。統一使用CFile的界面可以得到好的移植性。
MFC的文件類
MFC用一些類來封裝文件訪問的Win32 API。以CFile為基礎,從CFile派生出幾個類,如CStdioFile,CMemFile,MFC內部使用的CMiororFile,等等。
CFile的結構
CFile定義的枚舉類型
CFile類定義了一些和文件操作相關的枚舉類型,主要有四種:OpenFlags,Attribute,SeekPosition,hFileNull。下面,分別解釋這些枚舉類型。
OpenFlags
OpenFlags定義了13種文件訪問和共享模式:
enum OpenFlags {
//第一(從右,下同)至第二位,打開文件時訪問模式,讀/寫/讀寫
modeRead = 0x0000,
modeWrite = 0x0001,
modeReadWrite = 0x0002,
shareCompat = 0x0000, //32位MFC中沒用
//第五到第七位,打開文件時的共享模式
shareExclusive = 0x0010,//獨占方式,禁止其他進程讀寫
shareDenyWrite = 0x0020,//禁止其他進程寫
shareDenyRead = 0x0030,//禁止其他進程讀
shareDenyNone = 0x0040,//允許其他進程寫
//第八位,打開文件時的文件繼承方式
modeNoInherit = 0x0080,//不允許子進程繼承
//第十三、十四位,是否創建新文件和創建方式
modeCreate = 0x1000,//創建新文件,文件長度0
modeNoTruncate = 0x2000,//創建新文件時如文件已存在則打開
//第十五、十六位,文件以二進制或者文本方式打開,在派生類CStdioFile中用
typeText = 0x4000,
typeBinary = (int)0x8000
};
Attribute
Attribute定義了文件屬性:正常、只讀、隱含、系統文件,文件或者目錄等。
enum Attribute {
normal = 0x00,
readOnly = 0x01,
hidden = 0x02,
system = 0x04,
volume = 0x08,
directory = 0x10,
archive = 0x20
}
SeekPosition
SeekPosition定義了三種文件位置:頭、尾、當前:
enum SeekPosition{
begin = 0x0,
current = 0x1,
end = 0x2
};
hFileNull
hFileNull定義了空文件句柄
enum { hFileNull = -1 };
CFile的其他一些成員變量
CFile除了定義枚舉類型,還定義了一些成員變量。例如:
UINT m_hFile
該成員變量是public訪問屬性,保存::CreateFile返回的操作系統的文件句柄。MFC重載了運算符號HFILE來返回m_hFile,這樣在使用HFILE類型變量的地方可以使用CFile對象。
BOOL m_bCloseOnDelete;
CString m_strFileName;
這兩個成員變量是protected訪問屬性。m_bCloseOnDelete用來指示是否在關閉文件時刪除CFile對象;m_strFileName用來保存文件名。
CFile的成員函數
CFile的成員函數實現了對Win32文件操作函數的封裝,完成以下動作:打開、創建、關閉文件,文件指針定位,文件的鎖定與解鎖,文件狀態的讀取和修改,等等。其中,用到了m_hFile文件句柄的一般是虛擬函數,和此無關的一般是靜態成員函數。一般地,成員函數被映射到對應的Win32函數,如表11-1所示。
表11-1 CFile函數對Win32文件函數的封裝
虛擬 靜態 成員函數 對應的Win32函數 文件的創建、打開、關閉 √ Abort CloseHandle √ Duplicate DuplicateHandle √ Open CreateFile √ Close CloseHandle 文件的讀寫 √ Read ReadFile ReadHuge(向後兼容) 調用Read成員函數 √ Write WriteFile WriteHuage(向後兼容) 調用Write成員函數 √ Flush FlushFileBuffers 文件定位 √ Seek SetFilePointer SeekToBegin 調用Seek成員函數 SeekToEnd 調用Seek成員函數 √ GetLength 調用Seek成員函數 √ SetLength SetEndOfFile 文件的鎖定/解鎖 √ LockRange LockFile √ UnlockRange UnlockFile 文件狀態操作函數 √ GetPosition SetFilePointer GetStatus(CFileStatus&) GetFileTime,GetFileSize等 √ GetStatus(LPSTR lpszFileName CFileStatus&) FindFirstFile √ GetFileName 不是簡單地映射到某個函數 √ GetFileTitle √ GetFilePath √ SetFilePath √ SetStatus 改名和刪除 √ Rename MoveFile √ Remove DeleteFile
CFile的部分實現
這裡主要討論CFile對象的構造函數和文件的打開/創建的過程。
構造函數
CFile有如下幾個構造函數:
CFile()
缺省構造函數,僅僅構造一個CFile對象,還必須使用Open成員函數來打開文件。
CFile(int hFile)
已經打開了一個文件hFile,在此基礎上構造一個CFile對象來給它打包。HFile將被賦值給CFile的成員變量m_hFile。
CFile(LPCTSTR lpszFileName, UINT nOpenFlags)
指定一個文件名和文件打開方式,構造CFile對象,調用Open打開/創建文件,把文件句柄保存到m_hFile。
打開/創建文件
Open的原型如下:
BOOL CFile::Open(LPCTSTR lpszFileName, UINT nOpenFlags,
CFileException* pException)
Open調用Win32函數::CreateFile打開文件,並把文件句柄保存到成員變量m_hFile中。
CreateFile函數的原型如下:
HANDLE CreateFile(
LPCTSTR lpFileName,// pointer to name of the file
DWORD dwDesiredAccess,// access (read-write) mode
DWORD dwShareMode,// share mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes, //pointer to security descriptor
DWORD dwCreationDistribution,// how to create
DWORD dwFlagsAndAttributes,// file attributes
HANDLE hTemplateFile// handle to file with attributes to copy
);
顯然,Open必須把自己的兩個參數lpszFileName和nOpenFlags映射到CreateFile的七個參數上。---www.bianceng.cn
從OpenFlags的定義可以看出,(nOpenFlags & 3)表示了讀寫標識,映射成變量dwAccess,可以取值為Win32的GENERIC_READ、GENERIC_WRITE、GENERIC_READ|GENERIC_WRITE。
(nOpenFlags & 0x70)表示了共享模式,映射成變量dwShareMode,可以取值為Win32的FILE_SHARE_READ、FILE_SHARE_WRITE、FILE_SHARE_WRITE|FILE_SHARE_READ。
Open定義了一個局部的SECURITY_ATTRIBUTES變量sa,(nOpenFlags & 0x80)被賦值給sa.bInheritHandle。
(nOpenFlags & modeCreate)表示了創建方式,映射成變量dwCreateFlag,可以取值為Win32的OPEN_ALWAYS、CREATE_ALWAYS、OPEN_EXISTING。
在生成了上述參數之後,先調用::CreateFile:
HANDLE hFile =::CreateFile(lpszFileName,
dwAccess, dwShareMode, &sa,
dwCreateFlag, FILE_ATTRIBUTE_NORMAL, NULL);
然後,hFile被賦值給成員變量m_hFile,m_bCloseOnDelete被設置為TRUE。
由上可以看出,CFile打開(創建)一個文件時大大簡化了:: CreateFile函數的復雜性,即只需要指定一個文件名、一個打開文件的參數即可。若該參數指定為0,則表示以只讀方式打開一個存在的文件,獨占使用,不允許子進程繼承。
在CFile對象使用時,如果它是在堆中分配的,則應該銷毀它;如果在棧中分配的,則CFile對象將被自動銷毀。銷毀時析構函數被調用,析構函數是虛擬函數。若m_bCloseOnDelete為真且m_hFile非空,則析構函數調用Close關閉文件。
至於其他CFile成員函數的實現,這裡不作分析了。
CFile的派生類
這裡主要簡要地介紹CStdioFile和CmemFile及CFileFind。
CStdioFile
CStdioFile對文本文件進行操作。
CStdioFile定義了新的成員變量m_pStream,類型是FILE*。在打開或者創建文件時,使用_open_osfhandle從m_hFile(Win32文件句柄)得到一個“C”的FILE類型的文件指針,然後,在文件操作中,使用“C”的文件操作函數。例如,讀文件使用_fread,而不是::ReadFile,寫文件使用了_fwrite,而不是::WriteFile,等等。m_hFile是CFile的成員變量。
另外,CStdioFile不支持CFile的Dumplicate、LockRange、UnlockRange操作,但是實現了兩個新的操作ReadString和WriteString。
CMemFile
CMemFile把一塊內存當作一個文件來操作,所以,它沒有打開文件的操作,而是設計了Attach和Detach用來分配或者釋放一塊內存。相應地,它提供了Alloc、Free虛擬函數來操作內存文件,它覆蓋了Read、Write來讀寫內存文件。
CFileFind
為了方便文件查找,MFC把有關功能歸結成為一個類CFileFind。CFileFind派生於CObject類。首先,它使用FindFile和FineNextFile包裝了Win32函數::FindFirstFile和::FindNextFile;其次,它提供了許多函數用來獲取文件的狀態或者屬性。
使用CFileStatus結構來描述文件的屬性,其定義如下:
struct CFileStatus
{
CTime m_ctime; // 文件創建時間
CTime m_mtime; // 文件最近一次修改時間
CTime m_atime; // 文件最近一次訪問時間
LONG m_size; // 文件大小
BYTE m_attribute; // 文件屬性
BYTE _m_padding; // 沒有實際含義,用來增加一個字節
TCHAR m_szFullName[_MAX_PATH]; //絕對路徑
#ifdef _DEBUG
//實現Dump虛擬函數,輸出文件屬性
void Dump(CDumpContext& dc) const;
#endif
};
例如:
CFileStatus status;
pFile->GetStatus(status);
#ifdef _DEBUG
status.dump(afxDump);
#endif