檔案代表一系列的位元組。函數 fopen()將一個檔案和一個流關聯起來,並初始化一個型別為 FILE 的物件,該物件包含了控制該流的所有資訊。這些資訊包括指向緩衝區的指標;檔案位置指示器,它指定了獲取檔案的位置;以及指示錯誤和檔案結尾情況的標誌。
每個用於開啟檔案的函數(也就是 fopen()、freopen()和 tmpfile())都會返回一個指向 FILE 物件的指標,該 FILE 物件包含與被開啟檔案相關聯的流。一旦開啟了檔案,就可以呼叫函數傳遞資料並對流進行處理。這些函數都把指向 FILE 物件的指標(通常稱為 FILE 指標)作為它們的引數之一。FILE 指標指定了正在進行操作的流。
I/O 連結庫也包含了用於操作檔案系統的函數,這些函數把檔名作為它們的引數之一。使用這些函數不需要事先開啟檔案。它們包括:
(1)
函數 remove()刪除一個檔案(或者空目錄)。該字串引數是檔名。如果檔案具有多個名稱,那麼 remove()只會刪除所指定的名稱,而非刪除檔案本身。該檔案資料還可以通過別的方式來獲取,但是不能通過已刪除的檔名存取。
(2)
函數 rename()改變一個檔案(或目錄)的名稱。該函數的兩個字串引數依次為舊檔名和新檔名。函數 remove()和 rename()的返回值型別都是 int,成功時都會返回 0,失敗時都會返回非 0值,下面的語句將 songs.dat 重新命名為 mysong.dat:
if ( rename( "songs.dat", "mysongs.dat" ) != 0 )
fprintf( stderr, "Error renaming "songs.dat".n );
導致函數 rename()失敗的原因包括:使用舊檔名的檔案不存在;程式獲取檔案的許可權不夠;或者檔案已經被開啟。至於具體何種格式的檔名才是合法的,這是由實現版本決定。
無論是新檔案或已有檔案,首先必須開啟該檔案,才可以向檔案中寫入資料,或者修改其中的內容。
開啟一個檔案時,必須指定存取模式(access mode),以表明計劃對該檔案進行的是讀、寫或讀寫結合等操作。當使用完該檔案後,必須關閉它以釋放資源。
開啟檔案
標準庫提供函數 fopen()用以開啟檔案(在特殊情況下,還可以使用函數 freopen()和 tmpfile()來開啟檔案):
FILE *fopen( const char * restrict filename,
const char * restrict mode );
字串 filename 向該函數傳入所需開啟的檔案的名稱。該檔案名字符串也可以包含目錄資訊,但必須保證字串長度不得超過宏 FILENAME_MAX 中指定的最大長度。函數的第二個引數 mode 也是一個字串,用來指定檔案存取模式。
函數 freopen()會把檔案與一個新的流關聯起來。
FILE *freopen(const char * restrict filename,
const char * restrict mode,
FILE * restrict stream );
該函數將一個流重新定向。與 fopen()類似,freopen()也會用指定的存取模式開啟指定的檔案。但不同的是,
freopen()不會建立新的流,而是將檔案與已有的流關聯,已有的流通過該函數的第三個引數指定。之前與該流關聯的檔案會被關閉。freopen()常被用來重新定向到標準流 stdin、stdout 和 stderr。
FILE *tmpfile( void );
函數 tmpfile()會建立一個新的臨時檔案,其檔名與所有已有檔名都不一樣,然後開啟該檔案,進行二進位制資料的讀寫操作(類似於函數 fopen()採用“wb+”存取模式)。如果該程式正常地結束,該檔案會被自動刪除。
所有三個開啟檔案的函數 fopen()、freopen()和 tmpfile(),都會返回一個指標。如果成功,該指標就指向已開啟的流,如果失敗,該指標就為空指標。
如果一個檔案開啟用於寫操作,程式應賦予其獨立存取許可權以防止其他程式同時對該檔案進行寫操作。傳統的標準函數並不能確保獨立檔案存取許可權,但是 C11 新增的三個新“安全”函數 fopen_s()、freopen_s()和 tmpfile_s(),在作業系統支援的前提下,可以提供獨立存取許可權。
存取模式
函數 fopen()和 freopen()的第二個引數指定了檔案的存取模式,
存取模式決定了流所許可的輸入和輸出操作。對存取模式字串的許可值有嚴格的限制。該字串的第一個字元只能為三種形式:
r(表示“read”)、w(表示“write”)或者 a(表示“append”)。
在最簡單情況下,該字串只包含一個字元。
模式字串還可以包含 + 和 b(如果兩者同時具有,次序是沒有關係的,+b 效果等同於 b+)。
模式字串中的加號(+)表示讀寫操作都可以進行。然而,程式不可以在讀操作和寫操作之間立即作切換。在寫操作之後,必須呼叫函數 fflush()或者定位函數(fseek()、fsetpos()或 rewind()),然後才可以執行讀操作。在讀操作之後,必須呼叫定位函數,然後才可以執行寫操作。
模式字串中的 b 表示檔案以二進位制模式開啟。也就是說,與該檔案關聯的流是二進位制流。如果模式字串中沒有 b,新建立的流就是字串流。
當模式字串以 r 開始時,該檔案必須已經存在於檔案系統中。當模式字串以 w 開始時,如果檔案不存在,則會建立一個新檔案;如果檔案存在,該檔案當前內容會被清除,因為在“write”模式中,函數 fopen()將檔案長度設定為 0。
C11 新增一個功能,在作業系統支援的前提下,允許在獨立寫操作模式下開啟檔案。可以在以 w 起始的模式字串中使用字尾 x,例如 wx 或 w+bx,以指定獨立存取許可權。如果檔案已經存在或者不能被建立,則檔案開啟函數執行失敗(返回空指標)。否則,將建立檔案並以獨立存取許可權開啟它。
當模式字串以 a 開始時,如果檔案不存在,則也會建立一個新檔案。如果檔案存在,該檔案當前內容會被保留,因為所有新寫入的內容都會從檔案尾端新增。下面是一個簡單的範例:
#include <stdio.h>
#include <stdbool.h>
_Bool isReadWriteable( const char *filename )
{
FILE *fp = fopen( filename, "r+" ); // 開啟一個檔案以用於讀寫
if ( fp != NULL ) // fopen()是否執行成功
{
fclose(fp); // 成功:關閉檔案,沒有錯誤需要處理
return true;
}
else // 失敗
return false;
}
上例也展示了如何利用函數 fclose()關閉一個檔案。
關閉檔案
關閉檔案時需要使用函數 fclose(),該函數的原型是:
int fclose( FILE *fp );
該函數把緩衝區記憶體在的所有資料儲存到檔案中,關閉檔案,釋放所有用於該流輸入輸出緩衝區的記憶體。
函數 fclose()返回 0 表示成功,返回 EOF 表示產生錯誤。
當程式退出時,所有開啟的檔案都會自動關閉。儘管如此,還是應該在完成檔案處理後,主動關閉檔案。否則,一旦遇到非正常的程式終止,就可能會丟失資料。而且,一個程式可以同時開啟的檔案數量是有限的,數量上限小於等於常數 FOPEN_MAX 的值。