目錄
第1章讀寫文件
1.1 API
使用VC++讀寫文件,最直接、最高效的方法就是使用 Windows API,如:使用 CreateFile 打開文件,使用 WriteFile 寫文件,使用 ReadFile 讀文件……Windows 平台下,所有對文件的讀寫操作,最終都會調用這些 API 函數。使用 API 的效率最高,對文件讀寫的控制最強,缺點就是比較復雜,而且代碼沒有可移植性。
1.2 低級IO
為了方便移植 UNIX 的C代碼,VC++的C運行時庫實現了一套低級IO函數,如:_open、_write、_read……
1.2.1 文件序號
_open返回的是一個整數,MSDN上稱其為文件句柄(file handle),這與CreateFile返回的文件句柄容易混淆。為此,本文稱_open返回的為文件序號。
VC++中,系統預先打開了三個文件,其文件序號如下表所示
| 流 |
文件序號 |
說明 |
| stdin |
0 |
標准輸入設備,一般就是鍵盤 |
| stdout |
1 |
標准輸出設備,一般就是控制台 |
| stderr |
2 |
標准錯誤輸出設備,一般就是控制台 |
也就是說,無需調用_open,可以直接調用_write(1,"abc",3);往控制台輸出abc三個字符。
1.2.2 文本文件與二進制文件
讀寫二進制文件時,不會做任何處理,數據保持原樣。寫文本文件時,VC++會將換行符(即\n,0AH)替換為回車(即\r,0DH)和換行符;讀文本文件時,VC++會將\r\n替換為\n,並且在讀取到1AH時,認為文件結束。
_open函數里可以指定文件模式,
如:_open("c:\\1.txt",_O_RDONLY | _O_TEXT); //文本模式
如:_open("c:\\1.txt",_O_RDONLY | _O_BINARY); //二進制模式
如果_open函數里未指定 _O_TEXT 和 _O_BINARY,則以全局變量_fmode為准,如下面的代碼將以文本模式打開文件。
_fmode = _O_TEXT;
_open("c:\\1.txt",_O_RDONLY);
假定c:\1.txt的內容如下:
| 1 |
2 |
3 |
\r |
\n |
4 |
5 |
6 |
\r |
\n |
執行如下代碼:
| int n = _open("c:\\1.txt",_O_RDONLY | _O_BINARY); char str[128]; _read(n,str,128); _close(n); |
str的內容將是"123\r\n456\r\n"。將_O_BINARY替換為_O_TEXT,則str的內容將變為"123\n456\n"。可見\r\n如期的被替換為\n。不過,需要注意的是:將\r\n替換為\n是在第二個參數內進行的,如:將_read(n,str,128);替換為下面兩行代碼:
_read(n,str,4); //讀取到"123\r"
_read(n,str,4); //讀取到"\n456"
這個時候,\r\n就不會被替換為\n。這點要特別注意。
1.3 流IO
流IO函數有:fopen、fwrite、fread……它與低級IO最大的區別在於:低級IO無緩沖區,而流IO有緩沖區。如:同樣的寫文件,_write 會直接調用 WriteFile,而 fwrite 會將數據寫入緩沖區,達到一定數量后,再調用 WriteFile 將緩沖區內的數據寫入文件。顯然,流IO將大大減少調用 API 函數的次數,因此理論上其效率會比較高。
使用setvbuf函數,可以對緩沖區的大小、行為進行設置。
VC++里,流IO由低級IO實現。即:fopen會調用_open,fwrite會調用_write,fgets會調用_read函數……
流IO最重大的意義在於:它是符合ANSI C 標准的一套函數,可移植性最高。
1.4 Unicode
Windows的API函數一般會分為兩個版本,即處理ANSI字符串的窄字符版本,和處理Unicode字符串的寬字符版本。低級IO和流IO也有類似的寬字符版本函數,如:_wopen、_wfopen……
使用寬字符版本的IO函數之前,一定要調用setlocale(LC_ALL,""),設置 C 函數的代碼頁為系統默認的代碼頁,否則就有可能會出現亂碼。
1.5 流IO、低級IO、API之間的關系
三者的關系如下圖所示:流IO由低級IO實現,而低級IO由API實現。

函數_fileno可根據流IO的FILE*獲得低級IO的文件序號;
函數_get_osfhandle可根據低級IO的文件序號獲得API的文件句柄;
函數_open_osfhandle可根據API的文件句柄獲得低級IO的文件序號;
函數_fdopen可根據低級IO的文件序號獲得流IO的FILE*。
1.6 隨機讀寫
隨機讀寫是對文件讀寫的一種方式,具體表現為:
1、讀或寫時,會移動文件指針;
2、讀、寫操作交叉進行,如:讀了幾次后又寫幾次。
如果使用低級IO進行隨機讀寫,讀寫函數的處理是比較簡單的。反之,對於有緩沖區的流IO而言,隨機讀寫反而可能會降低文件的讀寫效率。因為以上兩個操作,將頻繁的清空緩沖區、移動文件指針。可以說,在隨機讀寫文件上,流IO並不比低級IO有優勢。
1.7 C++IO流
C++的STL中,可以使用 fstream、ifstream、ofstream……對文件進行讀寫。它的內部使用了流IO。
1.8 MFC
1.8.1 CFile
CFile是低級IO的C++實現。它直接調用 API,主要用於讀寫二進制文件。
1.8.2 CStdioFile
CStdioFile用於讀寫文本文件,具體的就是讀寫一行行的文本。它內部調用了流IO。當以 Unicode 編譯程序時,CStdioFile 將調用 Unicode 版本的流IO函數,此時一定要首先調用 setlocale(LC_ALL,"")。還需要說明的一點是:從 EVC3.0 至 VC2008,CStdioFile 都是無法正常運行在 Windows CE 平台上的。
1.8.3 CArchive
CArchive 主要用於串行化,它可以與 CFile 或 CMemFile 關聯,然后通過<<和>>方便的實現變量的串行化和反串行化。
CArchive 與 CFile 關聯,串行化時變量將保存至文件。
CArchive 與 CMemFile 關聯,串行化時變量將保存至內存。
CArchive 與CSocketFile關聯,串行化時變量將通過套接字傳輸到網絡。
1.9 總結
如果使用C語言,可以選擇使用API、低級IO、流IO。如果代碼要移植到其它平台,建議使用流IO。如果使用C++語言,上述方法均可使用。
讀寫二進制文件相對比較容易,比較復雜的是讀寫文本文件。復雜在兩個方面:
1、各個操作系統定義的行結束符不盡相同,如:Linux定義\r為行結束符,Mac定義\n為行結束符,Windows定義\r\n為行結束符。而且,還需要考慮各個操作系統之間的文件讀寫,如:Linux生成的文本文件在Windows下讀取……
2、必須考慮多種編碼。如:Windows平台下,文本文件分為四種格式:ANSI、UTF16LE、UTF16BE、UTF-8。VC++6.0只支持讀寫ANSI編碼的文本文件。自VC++2005開始,fopen增加了對UTF16LE、UTF-8這兩種編碼的支持,不過這種支持不適用於 Windows CE 平台。
上述兩個問題,最好的解決辦法就是自行編寫一個處理文本文件的C++類。
