C語言常用文件操作

文章出處:http://blog.csdn.net/thefutureisour/article/details/8133931

在C語言中,文件的操作是通過FILE結構體進行了,具體實現時,先利用fopen返回一個指向FILE結構體的指針:

FILE *fopen( const char *filename, const char *mode );
filename:文件名,mode:打開的模式,規定了是可讀、可寫、追加之類的屬性。
"r":可讀,如果文件不存在,fopen調用失敗
"w":可寫,如果文件存在,那麼原來的內容會被銷燬。
"a":在文件尾追加,在新的數據寫到文件裏之前,不改變EOF標記,如果文件不存在,創建一個新的文件。
"r+":可讀可寫,文件必須存在。
"w+":打開一個空文件用來讀寫,如果文件存在,則內容被銷燬。
"a+":可讀可追加,在新的數據寫到文件裏之前,改變EOF標記;如果文件不存在,創建一個新的文件。
如果調用失敗,返回一個空指針。

size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
buffer:寫入文件的內容;size:每一項的大小;count:寫入了多少項;stream:指向文件的指針。返回值爲寫入的總的字節數。

先看一個簡單的例子:

  1. int main()  
  2. {  
  3.     FILE *pFile = fopen("1.txt","w");  
  4.     fwrite("hello,world!",1,strlen("hello,world!"),pFile);  
  5.     return 0;  
  6. }  
這個程序看似平平,但還是大有文章可做:如果單步調試的話,執行完pFile後,就會在程序對應的文件夾下面產生一個名爲“1.txt”的文件(大小爲0);執行完fwrite後,大小還是爲0,;只有當程序完全運行完以後,hello,world!纔會被寫入文件。這是爲什麼呢?因爲C語言對文件的操作使用了緩衝文件系統:爲每個正在使用的文件開闢了一段內存,當我們向硬盤上寫數據時,是先寫到內存裏的(這與兼容DC有異曲同工之妙),直到內存滿了,或者是我們通知系統要關閉這個文件了,才把內存裏的數據拷貝到硬盤上。爲什麼這麼設計呢?因爲內存之間的操作速度要遠遠快於內存到硬盤的速度。如果我們每寫一個字符後就把它保存在硬盤上,代價太大了。回過頭來繼續看,當整個程序執行完後,關閉這個文件,此時,纔會把內存裏的數據拷貝到硬盤上。


其實,我們完全可以手動的關閉文件:
int fclose( FILE *stream );
stream:指向文件的指針。如果成功,返回0;失敗返回EOF。這樣,當執行完關閉文件後,文件裏面就有值了。


有時候,我們會反覆讀寫一個文件,而且每次讀寫後都希望立即看到結果。這時候每次讀完就關閉,然後重新打開的話實在太麻煩了,有沒有簡單的辦法呢?可以使用fflush來刷新流:
int fflush( FILE *stream );
stream:指向文件的指針。


如果我們在接着向文件中寫入數據:
fwrite("歡迎訪問",1,strlen("歡迎訪問"),pFile);
我們會發現,新寫入的數據會在原來文件的末尾後加上。可系統是如何知道原來文件的末尾在哪裏呢?
我們先看看FILE結構體:

  1. struct _iobuf {  
  2.         char *_ptr; //文件輸入的下一個位置   
  3.         int   _cnt; //當前緩衝區的相對位置   
  4.         char *_base;    //指基礎位置(應該是文件的其始位置)   
  5.         int   _flag;    //文件標誌  
  6.         int   _file;    //文件的有效性驗證  
  7.         int   _charbuf; //檢查緩衝區狀況,如果無緩衝區則不讀取  
  8.         int   _bufsiz;  //文件的大小  
  9.         char *_tmpfname;//臨時文件名  
  10.         };  
  11. typedef struct _iobuf FILE;  
注意,這只是VS2010中對FILE的實現!標準庫中並沒有規定FILE中必須是什麼樣的,之規定了我們可通過哪些函數去調用、訪問它。但是從這個實例中,我們可以看出:結構體的第一個成員是一個指向文件輸出的下一個位置的指針,我們可以通過fseek來移動文件的指針,它指向文件下一個要寫入的位置:

int fseek( FILE *stream, long offset, int origin );
stream:指向文件的指針,offset:偏移量;origin:初始位置,它有3種取法:
SEEK_CUR:當前位置
SEEK_END:文件尾
SEEK_SET:文件頭


如果我們這樣使用:

  1. fwrite("hello,world!",1,strlen("hello,world!"),pFile);  
  2. fflush(pFile);  
  3. fseek(pFile,0,SEEK_SET);  
  4. fwrite("歡迎訪問",1,strlen("歡迎訪問"),pFile);  
那麼文件中的內容就變爲了:“歡迎訪問rld!”

我們使用ftell函數獲取當前文件指針的位置:
long ftell( FILE *stream );
返回值爲與文件頭的偏移量。
通過它,我們可以獲得文件的長度。

讀取文件使用的是fread函數:
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
buffer指明瞭讀取的文件儲存在哪裏,size表明每個項的大小,count表明了讀多少項,stream是指向FILE類型的指針。返回值爲實際讀取的字節數。

當然,在你讀取文件之前,最好確保文件指針指向的是文件的頭部,通過rewind函數來讓指針復位:
void rewind( FILE *stream );
stream:指向文件的指針
下面看一個綜合的例子:
  1. int main()  
  2. {  
  3.     FILE *pFile = fopen("1.txt","w+");  
  4.     fwrite("hello,world!",1,strlen("hello,world!"),pFile);  
  5.     fflush(pFile);  
  6.     fseek(pFile,0,SEEK_SET);        //文件指針設爲起點,寫操作覆蓋原來的內容  
  7.     fwrite("歡迎訪問",1,strlen("歡迎訪問"),pFile);  
  8.     fseek(pFile,0,SEEK_END);        //文件指針設爲終點  
  9.     int len = ftell(pFile);         //獲取文件字節數  
  10.     char* ch = (char*)malloc(sizeof(char)* (len+1));    //分配內存,多一個字節  
  11.     memset(ch,0,(len+1));           //清0  
  12. //  fseek(pFile,0,SEEK_SET);        //文件指針指向頭部  
  13.     rewind(pFile);  
  14.     fread(ch,1,len,pFile);          //讀取文件  
  15.     fclose(pFile);  
  16.     printf("%s",ch);                  
  17.     return 0;  
  18. }  
需要說明一點,因爲%s是遇到一個空字符串後才停止的,所以我們分配的內存比文件的字節數多了一個,然後全部清0,這樣完成fread以後,剩下的那個字節剛好爲0,用來表示字符串的結束。




基本的內容就是這麼多,下面看一個細節問題:

  1. int main()  
  2. {  
  3.     FILE *pFile = fopen("2.txt","w+");  
  4.     char ch[3];  
  5.     ch[0] = 'a';  
  6.     ch[1] = 10;       
  7.     ch[2] = 'b';  
  8.     fwrite(ch,1,3,pFile);  
  9.     fflush(pFile);  
  10.     char buf[100];  
  11.     memset(buf,0,100);  
  12.     rewind(pFile);  
  13.     fread(buf,1,3,pFile);  
  14.     fclose(pFile);  
  15.     printf("%s",buf);  
  16.     return 0;  
  17. }  
我們明明寫了3個字符,爲什麼文件大小會是4個字節呢?
我們看看文件的16進制:61 0D 0A 62 ,其中61、62對應的是a和b,0A對應的是10,那麼0D對應的是什麼呢?答案是回車字符,這個字符是系統自動加進去的。而在讀取文件時,我們也並沒有讀取個字節,只用讀取3個字節,就能正確獲取內容了。
與這個問題相關的一組概念是:二進制文件和文本文件。C語言中,默認是以文本的方式打開文件的,如果我們使用二進制文件方式打開:
FILE *pFile = fopen("2.txt","w+b");

也不會出什麼問題,只不過文件的大小爲3個字節,對應的16進製爲:61 0A 62。但是如果你在寫入時使用的是文本文件,而讀取時使用的是二進制文件,就會出錯,因爲它會把回車當做一個字符輸出。總而言之,讀和寫的方式要對應。




發佈了5 篇原創文章 · 獲贊 5 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章