最近實在是太忙了,這篇整整就推遲了1個月了,實在是對不起。之前本打算這個模塊就結束了,文件操作就不寫了,但是文件操作又是一個很重要的東西,而且也剛好能夠總結之前我們學習的所有知識。同時也為了將文件操作這個初學者認為很神秘的東西給本質化。因此,本篇將逐一介紹C語言的文件操作。(本模塊的命名本來是想CC++一塊兒講解的,但是由於工作、畢業論文、業余時間的充電、還有要完成那個未知的夢等,因此因為時間問題C++就只能放在以後有機會再寫了,因此本篇將是本模塊的最後一篇,之後將不會再連載了,請大家諒解。)
好了,回到正題,先來看文件操作中的文件。所謂文件(file)一般指存儲在外部介質上數據的集合,比如我們經常使用的mp3、mp4、txt、bmp、jpg、exe、rmvb等等。這些文件各有各的用途,我們通常將它們存放在磁盤或者可移動盤等介質中。那麼,為什麼這裡面又有這麼多種格式的文件呢?原因很簡單,它們各有各的用途,區分就在於這些文件裡面存放的數據集合所遵循的存儲規則不一樣。舉個例子比如bmp圖片文件,為什麼他能夠表示一張圖片,因為它有固定的格式,哪一段到哪一段,哪個偏移到哪個偏移應該存放什麼數據是規定好了的。比如有文件頭,一般是一個結構體,存放的文件的一些信息,如圖片的大小,像素等等。再後來有數據區。然後我們要顯示一張圖片,就只需要按照前面所說的規則將文件頭結構和數據塊讀出來,然後將這些數據在屏幕上用顏色表示出來,就成了一張圖片。其它文件格式也類似。
這裡要說一個更重要的例子,對我們理解文件有好處。那麼這個文件就是exe文件(這裡只討論windows平台),通常我們認為它是一個可執行程序,這無疑是增加了它的神秘度。從本質上來講exe無非是一種固定的文件格式罷了。既然這樣,它就有一套自己的存儲規則。跟前面的圖片文件一樣有規則。此時,你可能會問:你這麼說那我就可以純手工(直接填寫數據填充文件)寫出一個exe可執行文件了? 面對你這個問題,我只能說你已經習慣思考了,已經習慣給自己提問了,已經很聰明了。那麼答案是肯定的,你完全可以用一個編輯器直接填寫數據寫出一個helloworld.exe文件或者helloworld.dll文件。因為這些具有一定格式規則的文件一般是二進制存儲的,於是我們可以用一個二進制編輯器新建一個二進制文件,然後向裡面填寫數據。然後雙擊運行輸出“helloworld”字符串。你可能會覺得很有成就感,我之前就寫過一個exe和dll。這裡exe和dll的文件格式也就是著名的PE文件格式。有興趣你可以去查閱相關資料,此非本文重點。
總結上面的認識,文件無非就是一段數據的集合,這些數據可以是有規則的集合,也可以是無序的集合。操作系統也就是以文件為單位對數據進行管理的。也就是說,要訪問外部介質上的數據,必須先按照文件名進行查找,然後從該文件中讀取數據。要想寫數據到外部介質,必須得建立一個文件,然後再寫入。因此,這樣來看,你眼前的文件將是一堆一堆數據而已,也沒有什麼類型文件之分了,類型只是為了區分而已,假如你把一個exe文件的擴展名改為txt,把它用記事本打開,同樣是可行的,只是會執行exe文件裡面的東西而已。(這裡又不得不提到一點,如果你是一名程序員或者愛好者,那麼你不應該將你的文件擴展名給隱藏了,要讓它顯示出來,如果你隱藏了,無非是增加了它的神秘感,同時在文件操作上不方便。通過上面的本質,我相信你能體會到我為什麼這麼說。)
說到這裡,你應該知道文件是什麼了,那麼再來看二進制文件和ASCII文本文件,為什麼要分為這兩種呢?
首先、文本文件方式存儲多用於我們需要明顯知道文件裡面的內容時,比如ini、h、c等文件都是文本文件,這種文件存儲的是字符(ASCII碼),比如一個整數10000,類型是short,占2字節,存儲文本形式將占用5個字節,一共5個字符。你可以想想更多的例子,體會文本文件方便之處(提示:這裡的文本文件不是說是txt文件,而是指所有以文本格式存儲的文件。)
其次、二進制文件方式多用於直接將內存裡面的數據形式原封不動存放到文件裡,比如上面的short 10000,在內存中占2字節,存儲內容為10000的二進制數,存到文件後還是占2字節,內容也是10000的二進制。這種方式可以整塊數據一塊兒存儲,同時還可以將內存數據映射到文件裡。
由上面兩點,C語言操作文件可以是字節流或者二進制流。它把數據看成是一連串字符(字節),而不需要考慮邊界。C語言對文件的存取是以字節為單位的。輸入輸出的數據流的開始和結束僅受程序控制而不受物理符號(如回車換行符)控制。這種文件通常稱為流式文件,大大增加了靈活性。我們可以產生很多自己的文件格式,在游戲程序裡面,用得比較多的就是資源包的格式,一般就是自定義的存取規則。我之前也寫了一個包文件,存取只需要遵循規則,原理是非常簡單的。大家可以試試在腦子裡面構造一個包文件。
在ANSI C標准中,使用的是“緩沖文件系統”。所謂緩沖文件系統指系統自動地在內存區為每一個正在使用的文件名開辟一個緩沖區,從內存向磁盤輸出數據必須先送到內存中的緩沖區,裝滿後再一起送到磁盤去。反向也是如此。這裡需要說明兩個詞:“輸入”“輸出”。輸入表示從文件裡讀數據到程序裡,輸出表示從程序裡寫數據到文件中。
了解了文件及文件存儲形式,下面該正式進入文件的讀寫了,不要太激動,還是慢慢來。細節往往決定成敗。在緩沖文件系統中,有一個很重要的一個東西就是文件指針,每個使用的文件都會在內存中開辟一個區,用於存放文件的有關信息,這些文件信息就保存在一個結構體變量中的,這個結構體是由系統定義的,名為FILE,先來看看VC2005在stdio.h下FILE結構體的定義:
struct _iobuf
{
char *_ptr; // 指向buffer中第一個未讀的字節
int _cnt; // 記錄剩余未讀字節的個數
char *_base; // 指向一個字符數組,即這個文件的緩沖
int _flag; // FILE結構所代表的打開文件的一些屬性
int _file; // 用於獲取文件描述,可以使用fileno函數獲得此文件的句柄。
int _charbuf; // 單字節的緩沖,即緩沖大小僅為1個字節,如果為單字節緩沖,_base將無效
int _bufsiz; // 記錄這個緩沖的大小
char *_tmpfname; // temporary file (i.e., one created by tmpfile()
// call). delete, if necessary (dont have to on
// Windows NT because it was done by the system when
// the handle was closed). also, free up the heap
// block holding the pathname.
};
typedef struct _iobuf FILE;
好了,上面的結構體就是這樣定義的。這裡不得不再次提到緩沖:
緩沖模式
常量(mode)
備注
無緩沖模式
_IONBF
該文件不使用任何緩沖,也可以說是字節緩沖
只能保存一個字節。
行緩沖模式
_IOLBF
僅對文本模式打開的文件有效,所謂行,即是指每收到一個換行符(
或
),就將緩沖flush掉
全緩沖模式
_IOFBF
僅當緩沖滿時才進行flush
上面結構體中的_flag就標記了緩沖的信息(我們關心這三個):
#define _IOYOURBUF 0x0100 // 使用用戶通過setbuf提供的buffer
#define _IOMYBUF 0x0008 //