和許多的C++程序一樣,有些人更喜歡用原先的C語言方式處理問題,如果你恰好也是這些人中的一員,就應該學習一下這篇文章。
基本的文件操作有
◆fopen——打開文件,指定文件以怎樣的方式打開(讀/寫)以及類型(二進制/文本)
◆fclose——關閉已經打開的文件
◆fread——讀取文件
◆fwrite——寫文件
◆fseek/fsetpos——將文件指示器轉移到文件中的某一地方
◆ftell/fgetpos——可以告訴你文件指示器所在的位置
文件有兩種基本類型:文本和二進制。在這兩者之中,通常二進制類型是較容易解決的。由於在文本中處理隨機存取並不常用,我們會在本文中重點關注二進制文件的處理。上面列出的操作中的前四項可用於文本文件和隨機存取文件。後面的兩項則僅用於隨機存取。
隨機存取意味著我們可以在文件的任意部分之間進行切換,且可以從中讀寫數據而不需要通讀整篇文件。
二進制文件
二進制文件是任意長度的文件,它保存有從0到0xff(0到255)不等的字節值。這些字節在二進制文件中沒有任何意義,與此不同的是,在文本文件中,值為13就意味著回車,10意味著換行,26意味著文件結束,而讀取文本文件的軟件要能夠解決這些問題。
在現在的術語中,我們將二進制文件稱為包含了字節的字符流,大多數語言傾向於將其理解為字符流而不是文件。重要的部分是數據流本身而不是其來源。在C語言中,你能從文件或數據流方面來考慮數據。或者,你可以將其理解為一組長的數組。通過隨機存取,你可以讀寫數組的任意部分。
例一:
// ex1.c : Defines the entry point for the console application.//
#include < stdio.h>
#include < string.h>
#include < windows.h>
int FileSuccess(FILE * handle,const char * reason, const char * path) {
OutputDebugString( reason );
OutputDebugString( path );
OutputDebugString(" Result : ");
if (handle==0)
{
OutputDebugString("Failed");
return 0;
}
else
{
OutputDebugString("Suceeded");
return 1;
}
}
int main(int argc, char * argv[])
{
const char * filename="test.txt";
const char * mytext="Once upon a time there were three bears.";
int byteswritten=0;
FILE * ft= fopen(filename, "wb");
if (FileSuccess(ft,"Opening File: ", filename)) {
fwrite(mytext,sizeof(char),strlen(mytext), ft);
fclose( ft );
}
printf("len of mytext = %i ",strlen(mytext));
return 0;
}
這段代碼顯示了一個簡單的打開待寫的二進制文件,文本字符(char*)會寫入該文件。通常你會使用文本文件但是筆者想證明你可以向二進制文件寫入文本。
// ex1.c#include < stdio.h>
#include < string.h>
int main(int argc, char * argv[])
{
const char * filename="test.txt";
const char * mytext="Once upon a time there were three bears.";
int byteswritten=0;
FILE * ft= fopen(filename, "wb") ;
if (ft) {
fwrite(mytext,sizeof(char),strlen(mytext), ft) ;
fclose( ft ) ;
}
printf("len of mytext = %i ",strlen(mytext)) ;
return 0;
}
例一的作用
這個例子打開了一個待寫的二進制文件。FILE*變量從fopen()調用中返回。如果這一操作失敗那麼它會返回為0。
Fopen()命令試圖打開指定的文件,在這個案例中則是位於相同文件夾的test.txt。記住,如果文件包含一個路徑那麼所有的退格必須重疊。“c:\folder\test.txt”是錯誤的,你必須使用“c:\\folder\\test.txt”。
由於文件樣式是wb,我們正准備寫入二進制文件。如果文件不存在則系統會創建一個文件,如果存在,則裡面的內容都會被刪除。如果調用fopen失敗了,或許由於文件被打開了,或者其名稱包括無效字符又或者一個無效路徑那麼fopen會返回0值。
雖然你可以只檢查ft是否為0(值為0則成功),但是筆者還是添加了一個FileSuccess()函數來確保這一操作。在窗口中,它會顯示調用是否成功以及文件名稱。如果你失敗了則可能需要修復。注意在Windows中一般沒有多少輸出文本可供系統調試器使用,
fwrite(mytext,sizeof(char),strlen(mytext), ft) ; fwrite()調用輸出了指定文本。第二,三個參數分別是字符的大小和字符串的長度。它們兩個都是被size_t定義了。注意有了二進制文件後,即便你正在向文件中寫入(char*)字符串,它也沒有任何附加換行字符。如果你想要這些字符,你必須明確將這些字符包含到字符串中。
讀寫文件
打開一個文件的時候,必須指定打開的方式。這意味著如果你打算要為文件附加東西,那麼是否要創建新文件並對其進行覆蓋?它是文本文件還是二進制文件呢?是要讀取文件還是要寫文件呢?這樣要通過使用一個或更多的文件模式分類符來完成,文件模式分類符是一些單獨的字母“r”,“b”,“w”,“a”和+。“r”意思是打開文件以便讀取。如果文件不存在或找不到文件這一操作會失敗。“w”意思是待寫方式或空文件方式打開文件。如果文件存在,則文件內容會被損壞。“a”表示打開文件,並准備從文件末端寫入而不需要在寫入新數據前刪除EOF標記;如果不存在該文件則首先會創建一個文件。向文件模型添加+會創建下列三種新模型:
“r+”打開文件等待讀取或寫入。“w+”以空文件方式打開文件等待讀取或寫入。如果文件存在,則文件內容會被損毀。
“a+”打開文件等待讀取或添加,添加的操作包括新數據寫入前EOF標記的移除,以及寫入完成後EOF標記的保存,如果文件不存在則先要創建文件。
下面的列表顯示了字碼組合包括文本的和二進制文件的。通常你可以選擇從文本文件中讀取或寫入文件,但是不要兩者同時使用。
就二進制文件而言,你可以選擇對相同文件進行讀取和寫入的操作。列表告訴了我們可以用字碼進行哪些操作。
Mode Type of file Read Write Create Truncate
r text Readrb+ binary Read
r+ text Read Write
r+b binary Read Write
rb+ binary Read Write
w text Write Create Truncate
wb binary Write Create Truncate
w+ text Read Write Create Truncate
w+b binary Read Write Create Truncate
wb+ binary Read Write Create Truncate
a text Write Create
ab binary Write Create
a+ text Read Write Create
a+b binary Write Create
ab+ binary Write Create
就筆者的經驗來看,除非你剛剛創建完文件或讀取完文件,否則你通過使用“w+b”只能僥幸成功。
還有一些情況允許其他字母存在。例如微軟運行“t”代表文本模式,“c”用於認可,“n”用於非認可,“S”為順序存取優化緩沖,“R”代表隨機存取,“T”代表臨時性而“D”用於 刪除/臨時性保存。
使用二進制文件的主要原因是可以獲得靈活性;你可以讀取或寫入文件的任意部分。文本文件只能讓你按照順序讀取或寫入。現在隨著SQLite或MySQL數據庫的普及,在二進制文件中使用隨機存取的需要減少了。從某種意義上說,隨機存取文件記錄有點老土了,但是仍然還是有用的。
筆者在數據庫普及之前就使用過基於隨機存取文件的多種數據處理方案。例如,在小文件中,筆者使用的是 索引/數據 文件模式。該模式包括兩個文件。一個是數據文件,它保存了一些長短不一的記錄。另一種文件是索引文件,這樣的文件擁有同樣的記錄作為數據文件。但是在索引文件中每個記錄的長度相同而且都由兩個適合結構的部分組成。
struct {??fpos_t pos;??int size; } indexrec;
類型fpos_t是由fsetpos()和fgetpos()定義和使用的執行。這些是fseek和ftell更新的版本且更有助於創建書簽。如果你正在計算文件聞之且需要設定文件那麼你應該使用fseek(),另外ftell()也可以給你int的當前位置。
在實際操作中,fpos_t可能只是一個int但是你應該使用fpos_t類型。它保存了當前文件指示器的副本。這是隨機存取文件的屬性,它表明了下一次讀取或寫入的位置。它的粒度為一,因此你可以將其放在文件的任意位置。