今天在做項目時聯想到了這兩個問題,所以實際編程測試了一下,有一些新的收穫:
<1> 我一直以為自己很熟悉如何使用C/C++中的二進位檔案,可今天測試的時候突然發現程式產生的二進位檔案和文字檔一樣。比如:
FILE* fp = fopen("binary","wb");
//FILE* fp = fopen("character.txt","w");
fprintf(fp,"count is %d",250);
上述代碼一個使用的是text file mode,一個是binary file mode,但結果產生的二進位檔案中儲存的仍然是ASCII碼,直接用記事本就可以開啟查看。要說區別,主要是分行符號的區別,binary file的分行符號為<LF>,而text file的分行符號為<CR><LF>,僅此而已,我就想怎麼回事啊,我明明是用二進位模式開啟的檔案呀,怎麼裡面直接儲存的還是ASCII碼?也就是說儲存125這個數字還是會佔用3個位元組,而不是我想象中那樣只佔一個位元組!
後來我就換用C++,結果還是一樣,代碼如下:
ofstream fs("binary",ios::binary);
//ofstream fs("character.txt");
int i = 32765;
fs<<i<<endl;
//fs.write((char*)&i,2);
fs.close();
無論以二進位檔案模式開啟還是以文字模式開啟,檔案中都是儲存著文本!似乎C/C++中的binary模式不起作用!?!
後來查閱資料才知道:要想在C/C++中將資料以二進位形式檔案輸出,與你開啟檔案時的模式沒有關係,關鍵是取決於你調用哪個函數往檔案裡寫東西!!只有使用fwrite和fs.write()函數才能以二進位形式輸出到檔案中,調用puts、fprintf、<<等函數輸出的都是ASCII文本,尤其需要注意的是類似於上面程式碼片段中那樣,在C++中,即使你用fs<<i<<endl語句來輸出一個整數,輸出到二進位檔案中的仍然是文字格式設定!<< operator在輸出之前會自動給你進行轉換,把一個整數值轉換成一位一位的數字字元!!而且我後來試過了,即使我以文字模式開啟一個檔案,假如我用fwrite函數輸出的話,檔案中仍然是二進位格式,呵呵,說明在輸出資料到檔案時,的確與開啟檔案的模式沒有關係,只與調用的輸出函數有關!!
此外,要注意,你不能用>>來讀取以二進位檔案格式儲存的整數!(註:中午我試過了,不行!這再次證明>>只能讀入文字格式設定的數字)
(補註:11:47am 我後來又想,所有上面的這些東西可以歸結成一句話:你以什麼模式開啟檔案根本不重要,因為你既改變不了檔案本身的內容,也改變不了C/C++中系統函數的工作方式,所以在編程的時候,你只要關心這個檔案裡的資料內容本身是二進位格式還是文字格式設定就好了!如果內容是文字格式設定的,你就調用文字格式設定那一套函數,比如puts,gets,fscanf,fprintf,<<,>>等,如果內容是二進位格式的,你就調用二進位格式那一套函數,比如fread,fwrite,ifstream.read(),ofstream.write()等。
只要保持檔案內容與處理函數相對應相一致就可以了,別管它用什麼模式開啟檔案!! 假如你用<<向一個二進位檔案中輸入一個整數,那麼其實裡面儲存的是文字格式設定的資料,那麼你就照樣可以以二進位模式開啟它,然後用>>來讀取這個整數。相反,如果你的二進位檔案裡面是一個以二進位形式儲存的整數,那你肯定不能用>>來讀取裡面的整數了!!
<2>關於位元組序的問題,我想用一張圖來表示就足夠了:
今天終於弄明白怎樣使用C++讀寫二進位檔案了。
要讀取檔案必須包含<fstream>標頭檔,這裡包含了C++讀寫檔案的方法。
可以使用fstream類,這個類可以對檔案進行讀寫操作。
1、開啟檔案。
開啟檔案可以有兩種方式,第一種可以使用fstream類的建構函式。
fstream file("test.dat",ios_base::in|ios_base::out|ios_base::app);
另外一種方法就是使用open函數。
fstream file;
file.open("test.dat",ios_base::in|ios_base::out|ios_base::app);
這樣就可以開啟一個可讀寫的檔案了。如果檔案不存在的話,就會建立一個新檔案並且以讀寫方式開啟。
這裡需要說明一點,如果檔案不存在的話,open函數中第二個參數必須包含ios_base::out|ios_base::app,
否則就不能正確建立檔案。
2、寫檔案。
先進性寫檔案的操作否則讀一個空檔案是沒有意義的。
既然是寫二進位檔案可以向檔案中寫入一個整形值。寫二進位字元只能使用write函數。
但是write函數的原形是write(const char * ch, int size)。第一個參數是char *類型,所以需要把將要寫入
檔案的int類型轉換成char *類型。這裡的轉換困擾了我好幾天,不過終於弄明白了。代碼如下。
int temp;
file.write((char *)(&temp),sizeof(temp));
3、讀檔案。
可以寫檔案了,讀檔案就好辦多了。讀檔案需要用到read函數。其參數和write大致相同,read(const char * ch, int size)。
要把內容讀到int類型變數中同樣涉及到一個類型轉換的問題。和寫檔案一樣。
int readInt;
file.read((char *)(&readInt),sizeof(readInt));
這樣檔案中的int值就讀入到int型變數readInt中了。
4、檔案指標。
在檔案的讀寫過程中往往需要對檔案進行選擇性讀取。所以需要進行檔案指標的移動。這是需要用到seekg和seekp函數。
在fstream類中有兩個檔案指標,一個是讀取檔案的指標,一個是寫檔案的指標分別用tellg和tellp檔案來取得指標的位置。
同樣seekg和seekp兩個函數分別是對這兩個指標進行移動的函數。這兩個函數的參數都是一樣的。
先對幾個枚舉類型進行一下說明:
ios_base::beg ——檔案開始位置
ios_base::cur ——檔案當前位置
ios_base::end ——檔案末尾位置
下面以seekg為例說明一下指標移動的方法:
file.seekg(3) ——指標移動到第三個字元的位置
file.seekg(ios_base::beg) ——指標移動到檔案開頭
file.seekg(ios_base::end) ——指標移動到檔案末尾
file.seekg(-3,ios_base::cur) ——指標當前位置向前移動三個字元
file.seekg(3,ios_base::cur) ——指標當前位置向後移動三個字元
file.seekg(3,file.tellg()) ——指標當前位置向後移動三個字元
file.seekg(file.tellg()+3) ——指標當前位置向後移動三個字元
5、對檔案操作完畢後別忘了關閉檔案。
file.close();
以上5個步驟就完成了對檔案的讀寫操作。文字檔的操作是相同的,比二進位檔案還要簡單。