Linux 檔案程式設計—fopen函式
阿新 • • 發佈:2019-01-10
1.2 檔案的輸入輸出函式 鍵盤、顯示器、印表機、磁碟驅動器等邏輯裝置, 其輸入輸出都可以通過檔案管理的方法來完成。而在程式設計時使用最多的要算是磁碟檔案, 因此本節主要以磁碟檔案為主, 詳細介紹Turbo C2.0提供的檔案操作函式, 當然這些對檔案的操作函式也適合於非磁碟檔案的情況。 另外, Turbo C2.0提供了兩類關於檔案的函式。一類稱做標準檔案函式也稱緩衝型檔案函式, 這是ANSI標準定義的函式; 另一類叫非標準檔案函式, 也稱非緩衝型檔案函式。這類函式最早公用於UNIX作業系統, 但現在MS-DOS3.0 以上版本的作業系統也可以使用。下面分別進行介紹。 1.2.1標準檔案函式 標準檔案函式主要包括檔案的開啟、關閉、讀和寫等函式。不象BASIC 、 FORTRAN語方有順序檔案和隨機檔案之分, 在開啟時就應按不同的方式確定。 Turbo C2.0並不區分這兩種檔案, 但提供了兩組函式, 即順序讀寫函式和隨機讀寫函式。 一、檔案的開啟和關閉 任何一個檔案在使用之前和使用之後, 必須要進行開啟和關閉, 這是因為作業系統對於同時開啟的檔案數目是有限制的, DOS作業系統中, 可以在DEVICE .SYS中定義允許同時開啟的檔案數n(用files=n定義)。其中n 為可同時開啟的檔案數, 一般n<=20。因此在使用檔案前應開啟檔案, 才可對其中的資訊進行存取。用完之後需要關閉, 否則將會出現一些意想不到的錯誤。Turbo C2.0提供了開啟和關閉檔案的函式。 1. fopen()函式 fopen函式用於開啟檔案, 其呼叫格式為: FILE *fopen(char *filename, *type); 在介紹這個函式之;前, 先了解一下下面的知識。 (1) 流(stream)和檔案(file) 流和檔案 在Turbo C2.0中是有區別的, Turbo C2.0 為程式設計者和被訪問的裝置之間提供了一層抽象的東西, 稱之為"流", 而將具體的實際裝置叫做檔案。流是一個邏輯裝置, 具有相同的行為。因此, 用來進行磁碟檔案寫的函式也同樣可以用來進行印表機的寫入。在Turbo C2.0中有兩種性質的流: 文字流( text stream)和二進位制(binary stream)。對磁碟來說就是文字檔案和二進位制檔案。本軟體為了便於讓讀者易理解Turbo C2.0語言而沒有對流和檔案作特別區分。 (2) 檔案指標FILE 實際上FILE是一個新的資料型別。它是Turbo C2.0的基本資料型別的集合, 稱之為結構指標。有關結構的概念將在第四節中詳細介紹, 這裡只要將FILE理解為一個包括了檔案管理有關資訊的資料結構, 即在開啟檔案時必須先定義一個檔案指標。 (3) 以後介紹的函式呼叫格式將直接寫出形式引數的資料型別和函式返回值的資料型別。例如: 上面開啟檔案的函式, 返回一個檔案指標, 其中形式引數有兩個, 均為字元型變數(字串陣列或字串指標)。本軟體不再對函式的呼叫格式作詳細說明。 現在再來看開啟檔案函式的用法。 fopen()函式中第一個形式引數表示檔名, 可以包含路徑和檔名兩部分。如: "B:TEST.DAT" "C://TC//TEST.DAT" 如果將路徑寫成"C:/TC/TEST.DAT"是不正確的, 這一點要特別注意。 第二個形式引數表示開啟檔案的型別。關於檔案型別的規定參見下表。 表 檔案操作型別 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 字元 含義 ──────────────────────────── "r" 開啟文字檔案只讀 "w" 建立文字檔案只寫 "a" 增補, 如果檔案不存在則建立一個 "r+" 開啟一個文字檔案讀/寫 "w+" 建立一個文字檔案讀/寫 "a+" 開啟或建立一個檔案增補 "b" 二進位制檔案(可以和上面每一項合用) "t" 文這檔案(預設項) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 如果要開啟一個CCDOS子目錄中, 檔名為CLIB的二進位制檔案, 可寫成: fopen("c://ccdos//clib", "rb"); 如果成功的開啟一個檔案, fopen()函式返回檔案指標, 否則返回空指標 (NULL)。由此可判斷檔案開啟是否成功。 2. fclose()函式 fclose()函式用來關閉一個由fopen()函式開啟的檔案 , 其呼叫格式為: int fclose(FILE *stream); 該函式返回一個整型數。當檔案關閉成功時, 返回0, 否則返回一個非零值。可以根據函式的返回值判斷檔案是否關閉成功。 例10: #iclude<stdio.h> main() { FILE *fp; /*定義一個檔案指標*/ int i; fp=fopen("CLIB", "rb"); /*開啟當前目錄名為CLIB的檔案只讀*/ if(fp==NULL) /*判斷檔案是否開啟成功*/ puts("File open error");/*提示開啟不成功*/ i=fclose(fp); /*關閉開啟的檔案*/ if(i==0) /*判斷檔案是否關閉成功*/ printf("O,K"); /*提示關閉成功*/ else puts("File close error");/*提示關閉不成功*/ } 二、有關檔案操作的函式 本節所講的檔案讀寫函式均是指順序讀寫, 即讀寫了一條資訊後, 指標自動加1。下面分別介紹寫操作函式和讀操作函式。 1. 檔案的順序寫函式 fprintf()、fputs()和fputc()函式 函式fprintf()、fputs()和fputc()均為檔案的順序寫操作函式, 其呼叫格式如下: int fprintf(FILE *stream, char *format, <variable-list>); int fputs(char *string, FILE *steam); int fputc(int ch, FILE *steam); 上述三個函式的返回值均為整型量。fprintf() 函式的返回值為實際寫入檔案中的字罕個數(位元組數)。如果寫錯誤, 則返回一個負數, fputs()函式返回0時表明將string指標所指的字串寫入檔案中的操作成功, 返回非0時, 表明寫操作失敗。fputc()函式返回一個向檔案所寫字元的值, 此時寫操作成功, 否則返回EOF(檔案結束結束其值為-1, 在stdio.h中定義)表示寫操作錯誤。 fprintf( ) 函式中格式化的規定與printf( ) 函式相同, 所不同的只是 fprintf()函式是向檔案中寫入。而printf()是向螢幕輸出。 下面介紹一個例子, 執行後產後一個test.dat的檔案。 例11: #include<stdio.h> main() { char *s="That's good news"); /*定義字串指標並初始化*/ int i=617; /*定義整型變數並初始化*/ FILE *fp; /*定義檔案指標*/ fp=fopne("test.dat", "w"); /*建立一個文字檔案只寫*/ fputs("Your score of TOEFLis", fp);/*向所建檔案寫入一串字元*/ fputc(':', fp); /*向所建檔案寫冒號:*/ fprintf(fp, "%d/n", i); /*向所建檔案寫一整型數*/ fprintf(fp, "%s", s); /*向所建檔案寫一字串*/ fclose(fp); /*關閉檔案*/ } 用DOS的TYPE命令顯示TEST.DAT的內容如下所示: 螢幕顯示 Your score of TOEFL is: 617 That's good news 2. 檔案的順序讀操作函式 fscanf()、fgets()和fgetc()函式 函式fscanf()、fgets()和fgetc()均為檔案的順序讀操作函式, 其呼叫格式如下: int fscanf(FILE *stream, char *format, <address-list>); char fgets(char *string, int n, FILE *steam); int fgetc(FILE *steam); fscanf()函式的用法與scanf()函式相似, 只是它是從檔案中讀到資訊。 fscanf()函式的返回值為EOF(即-1), 表明讀錯誤, 否則讀資料成功。fgets()函式從檔案中讀取至多n-1個字元(n用來指定字元數), 並把它們放入string指向的字串中, 在讀入之後自動向字串未尾加一個空字元, 讀成功返回string指標, 失敗返回一個空指標。fgetc()函式返回檔案當前位置的一個字元, 讀錯誤時返回EOF。 下面程式讀取例11產生的test.dat檔案, 並將讀出的結果顯示在螢幕上。 例12 #include<stdio.h> main() { char *s, m[20]; int i; FILE *fp; fp=fopen("test.dat", "r"); /*開啟文字檔案只讀*/ fgets(s, 24, fp); /*從檔案中讀取23個字元*/ printf("%s", s); /*輸出所讀的字串*/ fscanf(fp, "%d", &i); /*讀取整型數*/ printf("%d", i); /*輸出所讀整型數*/ putchar(fgetc(fp)); /*讀取一個字元同時輸出*/ fgets(m, 17, fp); /*讀取16個字元*/ puts(m); /*輸出所讀字串*/ fclose(fp); /*關閉檔案*/ getch(); /*等待任一鍵*/ } 執行後螢幕顯示: Your score of TOEFL is: 617 That's good news 如果將上例中fscanf(fp, "%d", &i)改為fscanf(fp, "%s", m), 再將其後的輸出語句改為printf("%s", m), 則可得出同樣的結果。由此可見Turbo C2. 0 中只要是讀文字檔案, 則不論是字元還是數字都將按其ASCII值處理。 另外還要說明的一點就是fscanf()函式讀到空白符時, 便自動結束, 在使用時要特別注意。 3. 檔案的隨機讀寫 有時使用者想直接讀取檔案中間某處的資訊, 若用檔案的順序讀寫必須從檔案頭開始直到要求的檔案位置再讀, 這顯然不方便。Turbo C2.0提供了一組檔案的隨機讀寫函式, 即可以將檔案位置指標定位在所要求讀寫的地方直接讀寫。 檔案的隨機讀寫函式如下: int fseek (FILE *stream, long offset, int fromwhere); int fread(void *buf, int size, int count, FILE *stream); int fwrite(void *buf, int size, int count, FILE *stream); long ftell(FILE *stream); fseek()函式的作用是將檔案的位置指標設定到從fromwhere開始的第offset 位元組的位置上, 其中fromwhere是下列幾個巨集定義之一: 檔案位置指標起始計算位置fromwhere ━━━━━━━━━━━━━━━━━━━━━━━━━━━ 符號常數 數值 含義 ─────────────────────────── SEEK_SET 0 從檔案開頭 SEEK_CUR 1 從檔案指標的現行位置 SEEK_END 2 從檔案末尾 ━━━━━━━━━━━━━━━━━━━━━━━━━━━ offset是指檔案位置指標從指定開始位置(fromwhere指出的位置)跳過的位元組數。它是一個長整型量, 以支援大於64K位元組的檔案。fseek()函式一般用於對二進位制檔案進行操作。 當fseek()函式返回0時表明操作成功, 返回非0表示失敗。 下面程式從二進位制檔案test_b.dat中讀取第8個位元組。 例13: #include<stdio.h> main() { FILE *fp; if((fp=fopen("test_b.dat", "rb"))==NULL) { printf("Can't open file"); exit(1); } fseek(fp, 8. 1, SEEK_SET); fgetc(fp); fclose(fp); } fread()函式是從檔案中讀count個欄位, 每個欄位長度為size個位元組, 並把它們存放到buf指標所指的緩衝器中。 fwrite()函式是把buf指標所指的緩衝器中, 長度為size個位元組的count個欄位寫到stream指向的檔案中去。 隨著讀和寫位元組數的增大, 檔案位置指示器也增大, 讀多少個位元組, 檔案位置指示器相應也跳過多少個位元組。讀寫完畢函式返回所讀和所寫的欄位個數。 ftell()函式返回檔案位置指示器的當前值, 這個值是指示器從檔案頭開始算起的位元組數, 返回的數為長整型數, 當返回-1時, 表明出現錯誤。 下面程式把一個浮點陣列以二進位制方式寫入檔案test_b.dat中。 例14: #include <stdio.h> main() { float f[6]={3.2, -4.34, 25.04, 0.1, 50.56, 80.5}; /*定義浮點陣列並初始化*/ int i; FILE *fp; fp=fopen("test_b.dat", "wb"); /*建立一個二進位制檔案只寫*/ fwrite(f, sizeof(float), 6, fp);/*將6個浮點數寫入檔案中*/ fclose(fp); /*關閉檔案*/ } 下面例子從test_b.dat檔案中讀100個整型數, 並把它們放到dat陣列中。 例15: #include <stdio.h> main() { FILE *fp; int dat[100]; fp=fopen("test_b.dat", "rb");/*開啟一個二進位制檔案只讀*/ if(fread(dat, sizeof(int), 100, fp)!=100) /*判斷是否讀了100個數*/ { if(feof(fp)) printf("End of file"); /*不到100個數檔案結束*/ else printf("Read error"); /*讀數錯誤*/ fclose(fp); /*關閉檔案*/ } 注意: 當用標準檔案函式對檔案進行讀寫操作時, 首先將所讀寫的內容放進緩衝區, 即寫函式只對輸出緩衝區進行操作, 讀函式只對輸入緩衝區進行操作。例如向一個檔案寫入內容, 所寫的內容將首先放在輸出緩衝區中, 直到輸出緩衝區存滿或使用fclose()函式關閉檔案時, 緩衝區的內容才會寫入檔案中。若無fclose() 函式, 則不會向檔案中存入所寫的內容或寫入的檔案內容不全。有一個對緩衝區進行重新整理的函式, 即fflush(), 其呼叫格式為: int fflush(FILE *stream); 該函式將輸出緩衝區的內容實際寫入檔案中, 而將輸入緩衝區的內容清除掉。 4. feof()和rewind()函式 這兩個函式的呼叫格式為: int feof(FILE *stream); int rewind(FILE *stream); feof()函式檢測檔案位置指示器是否到達了檔案結尾, 若是則返回一個非0 值, 否則返回0。這個函式對二進位制檔案操作特別有用, 因為二進位制檔案中, 檔案結尾標誌EOF也是一個合法的二進位制數, 只簡單的檢查讀入字元的值來判斷檔案是否結束是不行的。如果那樣的話, 可能會造成檔案未結尾而被認為結尾, 所以就必須有feof()函式。 下面的這條語句是常用的判斷檔案是否結束的方法。 while(!feof(fp)) fgetc(fp); while為迴圈語句, 將在下面介紹。 rewind()函式用於把檔案位置指示器移到檔案的起點處, 成功時返回0, 否則, 返回非0值。 1.2.2 非標準檔案函式 這類函式最早用於UNIX作業系統, ANSI標準未定義, 但有時也經常用到, DOS 3.0以上版本支援這些函式。它們的標頭檔案為io.h。 一、檔案的開啟和關閉 1. open()函式 open()函式的作用是開啟檔案, 其呼叫格式為: int open(char *filename, int access); 該函式表示按access的要求開啟名為filename的檔案, 返回值為檔案描述字, 其中access有兩部分內容: 基本模式和修飾符, 兩者用" "("或")方式連線。修飾符可以有多個, 但基本模式只能有一個。access的規定如表3-2。 表3-2 access的規定 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 基本模式 含義 修飾符 含 義 ──────────────────────────── O_RDONLY 只讀 O_APPEND 檔案指標指向末尾 O_WRONLY 只寫 O_CREAT 檔案不存在時建立檔案, 屬性按基本模式屬性 O_RDWR 讀寫 O_TRUNC 若檔案存在, 將其長度 縮為0, 屬性不變 O_BINARY 開啟一個二進位制檔案 O_TEXT 開啟一個文字檔案 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━ open()函式開啟成功, 返回值就是檔案描述字的值(非負值), 否則返回-1。 2. close()函式 close()函式的作用是關閉由open()函式開啟的檔案, 其呼叫格式為: int close(int handle); 該函式關閉檔案描述字handle相連的檔案。 二、讀寫函式 1. read()函式 read()函式的呼叫格式為: int read(int handle, void *buf, int count); read()函式從handle(檔案描述字)相連的檔案中, 讀取count個位元組放到buf 所指的緩衝區中, 返回值為實際所讀位元組數, 返回-1表示出錯。返回0 表示檔案結束。 2. write()函式 write()函式的呼叫格式為: int write(int handle, void *buf, int count); write()函式把count個位元組從buf指向的緩衝區寫入與handle相連的檔案中, 返回值為實際寫入的位元組數。 三、隨機定位函式 1. lseek()函式 lseek()函式的呼叫格式為: int lseek(int handle, long offset, int fromwhere); 該函式對與handle相連的檔案位置指標進行定位, 功能和用法與fseek() 函式相同。 2. tell()函式 tell()函式的呼叫格式為: long tell(int handle); 該函式返回與handle相連的檔案現生位置指標, 功能和用法與ftell()相同。