C語言中對檔案操作的小結
阿新 • • 發佈:2019-02-17
在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:指向檔案的指標。返回值為寫入的總的位元組數。
其實,我們完全可以手動的關閉檔案:
int fclose( FILE *stream );
stream:指向檔案的指標。如果成功,返回0;失敗返回EOF。這樣,當執行完關閉檔案後,檔案裡面就有值了。
有時候,我們會反覆讀寫一個檔案,而且每次讀寫後都希望立即看到結果。這時候每次讀完就關閉,然後重新開啟的話實在太麻煩了,有沒有簡單的辦法呢?可以使用fflush來重新整理流:
int fflush( FILE *stream );
stream:指向檔案的指標。
如果我們在接著向檔案中寫入資料:
fwrite("歡迎訪問",1,strlen("歡迎訪問"),pFile);
我們會發現,新寫入的資料會在原來檔案的末尾後加上。可系統是如何知道原來檔案的末尾在哪裡呢?
我們先看看FILE結構體:
我們使用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:指向檔案的指標
下面看一個綜合的例子:
基本的內容就是這麼多,下面看一個細節問題:
我們看看檔案的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。但是如果你在寫入時使用的是文字檔案,而讀取時使用的是二進位制檔案,就會出錯,因為它會把回車當做一個字元輸出。總而言之,讀和寫的方式要對應。
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:指向檔案的指標。返回值為寫入的總的位元組數。
先看一個簡單的例子:
int main()
{
FILE *pFile = fopen("1.txt","w");
fwrite("hello,world!",1,strlen("hello,world!"),pFile);
return 0;
}
這個程式看似平平,但還是大有文章可做:如果單步除錯的話,執行完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結構體:
注意,這只是VS2010中對FILE的實現!標準庫中並沒有規定FILE中必須是什麼樣的,之規定了我們可通過哪些函式去呼叫、訪問它。但是從這個例項中,我們可以看出:結構體的第一個成員是一個指向檔案輸出的下一個位置的指標,我們可以通過fseek來移動檔案的指標,它指向檔案下一個要寫入的位置:struct _iobuf { char *_ptr; //檔案輸入的下一個位置 int _cnt; //當前緩衝區的相對位置 char *_base; //指基礎位置(應該是檔案的其始位置) int _flag; //檔案標誌 int _file; //檔案的有效性驗證 int _charbuf; //檢查緩衝區狀況,如果無緩衝區則不讀取 int _bufsiz; //檔案的大小 char *_tmpfname;//臨時檔名 }; typedef struct _iobuf FILE;
int fseek( FILE *stream, long offset, int origin );
stream:指向檔案的指標,offset:偏移量;origin:初始位置,它有3種取法:
SEEK_CUR:當前位置
SEEK_END:檔案尾
SEEK_SET:檔案頭
如果我們這樣使用:
那麼檔案中的內容就變為了:“歡迎訪問rld!”fwrite("hello,world!",1,strlen("hello,world!"),pFile); fflush(pFile); fseek(pFile,0,SEEK_SET); fwrite("歡迎訪問",1,strlen("歡迎訪問"),pFile);
我們使用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:指向檔案的指標
下面看一個綜合的例子:
int main()
{
FILE *pFile = fopen("1.txt","w+");
fwrite("hello,world!",1,strlen("hello,world!"),pFile);
fflush(pFile);
fseek(pFile,0,SEEK_SET); //檔案指標設為起點,寫操作覆蓋原來的內容
fwrite("歡迎訪問",1,strlen("歡迎訪問"),pFile);
fseek(pFile,0,SEEK_END); //檔案指標設為終點
int len = ftell(pFile); //獲取檔案位元組數
char* ch = (char*)malloc(sizeof(char)* (len+1)); //分配記憶體,多一個位元組
memset(ch,0,(len+1)); //清0
// fseek(pFile,0,SEEK_SET); //檔案指標指向頭部
rewind(pFile);
fread(ch,1,len,pFile); //讀取檔案
fclose(pFile);
printf("%s",ch);
return 0;
}
需要說明一點,因為%s是遇到一個空字串後才停止的,所以我們分配的記憶體比檔案的位元組數多了一個,然後全部清0,這樣完成fread以後,剩下的那個位元組剛好為0,用來表示字串的結束。基本的內容就是這麼多,下面看一個細節問題:
int main()
{
FILE *pFile = fopen("2.txt","w+");
char ch[3];
ch[0] = 'a';
ch[1] = 10;
ch[2] = 'b';
fwrite(ch,1,3,pFile);
fflush(pFile);
char buf[100];
memset(buf,0,100);
rewind(pFile);
fread(buf,1,3,pFile);
fclose(pFile);
printf("%s",buf);
return 0;
}
我們明明寫了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。但是如果你在寫入時使用的是文字檔案,而讀取時使用的是二進位制檔案,就會出錯,因為它會把回車當做一個字元輸出。總而言之,讀和寫的方式要對應。