檔案的輸入輸出函數
鍵盤、顯示器、印表機、磁碟機等邏輯裝置, 其輸入輸出都可以通過文
件管理的方法來完成。而在編程時使用最多的要算是磁碟檔案, 因此本節主要以
磁碟檔案為主, 詳細介紹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的基礎資料型別 (Elementary Data Type)的集合,
稱之為結構指標。有關結構的概念將在第四節中詳細介紹, 這裡只要將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 指標
(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指向的
字串中, 在讀入之後自動向字串未尾加一個Null 字元, 讀成功返回string指標,
失敗返回一個null 指標。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()相同。