工作中碰到一個問題,如何只修改檔案中間的幾個位元組,而其他的內容不變。這個問題看似簡單,但是很多人估計都不知道怎麼做。我開始seek到檔案的特定的位置,然後寫檔案,但是使用的檔案開啟模式不對,檔案不是被清空,就是被截斷,達不到效果。
fopen的開啟模式
在C語言中檔案開啟方式有這麼幾種:
- r 以只讀方式開啟檔案,只能讀不能寫,往檔案中寫是沒有任何效果的
- r+ 可以讀,也可以寫,檔案開啟的時候,指向檔案開頭,可以通過seek改變讀寫位置
- w 這種方式開啟的檔案控制程式碼,只能寫,如果檔案存在則將長度清零,否則新建檔案,這種控制程式碼通過seek之後,seek位置之前的檔案資料全部變成0x00
- w+ 同w選項,只不過多了一個可讀功能
- a 這種方式開啟的檔案,可以寫,但是位置在檔案末尾,即使往回seek也沒有用,資料還是從檔案末尾開始附加
- a+ 同a選項,多了可讀的功能
另外還有2個選項,可以與上面的6個選項複合使用,一個是t表示以文字的方式開啟檔案(預設是t),一個是b表示以二進位制的方式開啟檔案,t和b是互斥的不能同時使用。當與b組合時,有這麼幾種方式:wb、ab、rb、wb+、ab+、rb+,而a,w,r這幾個選項是不能組合使用的,其中a,w都表示寫檔案,只不過一個在檔案尾,一個在檔案開始處,r表示讀檔案。我試過將a,w,r幾個兩兩組合使用,發現下面的現象:
- wr 與w效果一樣
- rw與r效果一樣
- aw與a效果一樣
- wa 與w效果一樣
- ar與a效果一樣
- ra與r效果一樣
可以看出來當a,w,r在一起組合使用的時候,其後面的選項實際上好像是被忽略了
問題的解決方法:rb+開啟檔案
所以解決文章開頭提出來的問題,應該使用 rb+ 的方式開啟檔案,這種方式開啟的檔案,可讀,可寫,開啟之後寫指標在檔案開始處,可以任意seek,而seek之後寫的內容會覆蓋被寫的內容,其他沒有寫到的內容不會有改變。
測試程式
//程式測試結果在ubuntu linux下執行獲得 #include <stdio.h> #include <string.h> int main() { //檔案原始資料 //00 01 02 03 04 05 06 07 08 09 //下面每一個fopen前面註釋中的資料是以該方式開啟檔案,寫檔案之後檔案的內容 //00 00 00 00 CC DD //FILE * file = fopen("./test.data","wb+"); //00 00 00 00 CC DD //FILE * file = fopen("./test.data","wb"); //00 01 02 03 CC DD 06 07 08 09 FILE * file = fopen("./test.data","rb+"); //這種是正確的做法 //00 01 02 03 04 05 06 07 08 09 //FILE * file = fopen("./test.data","rb"); //00 01 02 03 04 05 06 07 08 09 CC DD //FILE * file = fopen("./test.data","ab"); //00 01 02 03 04 05 06 07 08 09 CC DD //FILE * file = fopen("./test.data","ab+"); //00 00 00 00 CC DD //FILE * file = fopen("./test.data","wr"); //00 01 02 03 04 05 06 07 08 09 //FILE * file = fopen("./test.data","rw"); //00 01 02 03 04 05 06 07 08 09 CC DD //FILE * file = fopen("./test.data","aw"); //00 00 00 00 CC DD //FILE * file = fopen("./test.data","wa"); //00 01 02 03 04 05 06 07 08 09 CC DD //FILE * file = fopen("./test.data","ar"); //00 01 02 03 04 05 06 07 08 09 //FILE * file = fopen("./test.data","ra"); if(file!=NULL) { char buffer[]={0xCC,0xDD}; fseek(file,4,SEEK_SET); fwrite(buffer,1,sizeof(buffer),file); fclose(file); } return 0; }