第十三章 文 件 13.1 C文件概述 文件:文件指存儲在外部介質(zhì)(如磁盤磁帶)上數(shù)據(jù)的集合。 操作系統(tǒng)是以文件為單位對數(shù)據(jù)進(jìn)行管理的。
文件的分類 ●從用戶觀點(diǎn): 特殊文件(標(biāo)準(zhǔn)輸入輸出文件或標(biāo)準(zhǔn)設(shè)備文件)。 普通文件(磁盤文件)。 ●從操作系統(tǒng)的角度看,每一個(gè)與主機(jī)相連的輸入 輸出設(shè)備看作是一個(gè)文件。 例:輸入文件:終端鍵盤 輸出文件:顯示屏和打印機(jī) (1)根據(jù)文件的內(nèi)容,可分為程序文件和數(shù)據(jù)文件,程序文件又可分為源文件、目標(biāo)文件和可執(zhí)行文件。 (2)根據(jù)文件的組織形式,可分為順序存取文件和隨機(jī)存取文件。 (3)根據(jù)文件的存儲形式,可分為ASCII碼文件和二進(jìn)制文件。 ASCII碼文件的每1個(gè)字節(jié)存儲1個(gè)字符,因而便于對字符進(jìn)行逐個(gè)處理。但一般占用存儲空間較多,而且要花費(fèi)轉(zhuǎn)換時(shí)間(二進(jìn)制與ASCII碼之間的轉(zhuǎn)換)。 二進(jìn)制文件是把內(nèi)存中的數(shù)據(jù),原樣輸出到磁盤文件中。可以節(jié)省存儲空間和轉(zhuǎn)換時(shí)間,但1個(gè)字節(jié)并不對應(yīng)1個(gè)字符,不能直接輸出字符形式。 C語言對文件的處理方法: 緩沖文件系統(tǒng):系統(tǒng)自動(dòng)地在內(nèi)存區(qū)為每一個(gè)正在使用的文件開辟一個(gè)緩沖區(qū)。用緩沖文件系統(tǒng)進(jìn)行的輸入輸出又稱為高級磁盤輸入輸出。 非緩沖文件系統(tǒng):系統(tǒng)不自動(dòng)開辟確定大小的緩沖區(qū),而由程序?yàn)槊總€(gè)文件設(shè)定緩沖區(qū)。用非緩沖文件系統(tǒng)進(jìn)行的輸入輸出又稱為低級輸入輸出系統(tǒng)。 說明: 在UNIX系統(tǒng)下,用緩沖文件系統(tǒng)來處理文本文件,用非緩沖文件系統(tǒng)來處理二進(jìn)制文件。 ANSI C 標(biāo)準(zhǔn)只采用緩沖文件系統(tǒng)來處理文本文件和二進(jìn)制文件。 C語言中對文件的讀寫都是用庫函數(shù)來實(shí)現(xiàn)。 13.2 文件類型指針 Turbo C在stdio.h文件中有以下的文件類型聲明: typedef struct { shortlevel; /*緩沖區(qū)“滿”或“空”的程度*/ unsignedflags; /*文件狀態(tài)標(biāo)志*/ charfd; /*文件描述符*/ unsignedcharhold; /*如無緩沖區(qū)不讀取字符*/ shortbsize; /*緩沖區(qū)的大小*/ unsignedchar*buffer;/*數(shù)據(jù)緩沖區(qū)的位置*/ unsignedar*curp;/*指針,當(dāng)前的指向*/ unsignedistemp;/*臨時(shí)文件,指示器*/ shorttoken;/*用于有效性檢查*/}FILE; 在緩沖文件系統(tǒng)中,每個(gè)被使用的文件都要在內(nèi)存中開辟一 FILE類型的區(qū),存放文件的有關(guān)信息。 FILE類型的數(shù)組: FILE f[5];定義了一個(gè)結(jié)構(gòu)體數(shù)組f,它有5個(gè)元素,可以用來存放5個(gè)文件的信息。 文件型指針變量: FILE *fp;fp是一個(gè)指向FILE類型結(jié)構(gòu)體的指針變量。可以使fp指向某一個(gè)文件的結(jié)構(gòu)體變量,從而通過該結(jié)構(gòu)體變量中的文件信息能夠訪問該文件。如果有n個(gè)文件,一般應(yīng)設(shè)n個(gè)指針變量,使它們分別指向n個(gè)文件,以實(shí)現(xiàn)對文件的訪問。 13.3 文件的打開與關(guān)閉 一.文件的打開(fopen函數(shù)) 函數(shù)調(diào)用: FILE *fp; fp=fopen(文件名,使用文件方式); ①需要打開的文件名,也就是準(zhǔn)備訪問的文件的名字; ②使用文件的方式(“讀”還是“寫”等); ③讓哪一個(gè)指針變量指向被打開的文件。
二.文件的關(guān)閉(fclose函數(shù)) 函數(shù)調(diào)用: fclose(文件指針); 函數(shù)功能: 使文件指針變量不指向該文件,也就是文件指針變量與文件“脫鉤”,此后不能再通過該指針對原來與其相聯(lián)系的文件進(jìn)行讀寫操作。 返回值: 關(guān)閉成功返回值為0;否則返回EOF(-1) 。 13.4 文件的讀寫 一、字符輸入輸出函數(shù)(fputs()和fgets()) 1.fputs函數(shù) 函數(shù)調(diào)用: fputs ( ch,fp ) ; 函數(shù)功能: 將字符(ch的值)輸出到fp所指向的文件中去。 返回值: 如果輸出成功,則返回值就是輸出的字符; 如果輸出失敗,則返回一個(gè)EOF。 2.fgets函數(shù) 函數(shù)調(diào)用: ch=fgets(fp); 函數(shù)功能: 從指定的文件讀入一個(gè)字符,該文件必須是以讀或讀寫方式打開的。 返回值: 讀取成功一個(gè)字符,賦給ch。 如果遇到文件結(jié)束符,返回一個(gè)文件結(jié)束標(biāo)志EOF 。 常見的讀取字符操作 從一個(gè)文本文件順序讀入字符并在屏幕上顯示出來: ch = fgetc(fp); while(ch!=EOF) { putchar(ch); ch = fgetc(fp); } 注意:EOF不是可輸出字符,因此不能在屏幕上顯示。由于字符的ASCII碼不可能出現(xiàn)-1,因此EOF定義為-1是合適的。當(dāng)讀入的字符值等于-1時(shí),表示讀入的已不是正常的字符而是文件結(jié)束符。 常見的讀取字符操作 從一個(gè)二進(jìn)制文件順序讀入字符: while(!feof(fp)) { ch = fgetc(fp); } 注意:ANSI C提供一個(gè)feof()函數(shù)來判斷文件是否真的結(jié)束。如果是文件結(jié)束,函數(shù)feof(fp)的值為1(真);否則為0(假)。以上也適用于文本文件的讀取。 #include <stdlib.h> #include <stdio.h> void main(void) { FILE *fp; char ch,filename[10]; scanf("%s",filename); if((fp=fopen(filename,"w"))==NULL) { printf("cannot open file\n"); exit(0); /*終止程序*/ } ch=getchar( ); /*接收執(zhí)行scanf語句時(shí)最后輸入的回車符 */ ch=getchar( ); /* 接收輸入的第一個(gè)字符 */ while(ch!='#’) { fputc(ch,fp);putchar(ch); ch=getchar(); } fclose(fp); }
#include <stdlib.h> #include <stdio.h> main( ) { FILE *in,*out; char ch,infile[10],outfile[10]; printf("Enter the infile name:\n"); scanf("%s",infile); printf("Enter the outfile name:\n"); scanf("%s",outfile); if((in=fopen(infile,"r"))==NULL) { printf("cannot open infile\n"); exit(0); } if((out=fopen(outfile,"w"))==NULL) { printf("cannot open outfile\n"); exit(0); } while(!feof(in))fputc(fgetc(in),out); close(in); fclose(out); }
#include <stdlib.h> #include <stdio.h> main(int argc,char *argv[ ]) { FILE *in,*out; char ch; if (argc!=3) { printf("You forgot to enter a filename\n"); exit(0); } if((in=fopen(argv[1],"rb"))==NULL) { printf("cannot open infile\n"); exit(0); } if((out=fopen(argv[2],"wb"))==NULL) { printf("cannot open outfile\n"); exit(0); } while(!feof(in)) fputc(fgetc(in),out); fclose(in); fclose(out); }
二、數(shù)據(jù)塊讀寫函數(shù)(fread()和fwrite()) 函數(shù)調(diào)用: fread (buffer,size,count,fp); fwrite(buffer,size,count,fp); 參數(shù)說明: buffer:是一個(gè)指針。 對fread 來說,它是讀入數(shù)據(jù)的存放地址。 對fwrite來說,是要輸出數(shù)據(jù)的地址(均指起始地址)。 size: 要讀寫的字節(jié)數(shù)。 count: 要進(jìn)行讀寫多少個(gè)size字節(jié)的數(shù)據(jù)項(xiàng)。 fp: 文件型指針。 使用舉例: 若文件以二進(jìn)制形式打開: fread(f,4,2,fp); 此函數(shù)從fp所指向的文件中讀入2個(gè)4個(gè)字節(jié)的數(shù)據(jù),存儲到數(shù)組f中。 使用舉例: 若有如下結(jié)構(gòu)類型: struct student_type { char name[10]; int num; int age; char addr[30];}stud[40]; } 可以用fread和fwrite來進(jìn)行數(shù)據(jù)的操作: for(i=0;i<40;i++) fread(&stud[i],sizeof(struct student-type),1,fp); for(i=0;i<40,i++) fwrite(&stud[i],sizeof(struct student-type),1,fp); 使用舉例: 例13.3從鍵盤輸入4個(gè)學(xué)生的有關(guān)數(shù)據(jù),然后把它們轉(zhuǎn)存到磁盤文件上去。 #include <stdio.h> #define SIZE 4 struct student_type { char name[10]; int num; int age; char addr[15]; }stud[SIZE]; /*定義結(jié)構(gòu)*/ void save( ) { FILE *fp; int i; if((fp=fopen("stu-list","wb"))==NULL) { printf("cannot open file\n"); return; } for(i=0;i<SIZE;i++)/*二進(jìn)制寫*/ if(fwrite(&stud[i],sizeof(struct student_type),1,fp)!=1) printf(“file write error\n”);/*出錯(cuò)處理*/ fclose(fp); } /*關(guān)閉文件*/ main() { int i; for(i=0;i<SIZE;i++)/*從鍵盤讀入學(xué)生信息*/ scanf("%s%d%d%s",stud[i].name,&stud[i].num,&stud[i].age,stud[i].addr); save( ); }/*調(diào)用save()保存學(xué)生信息*/
#include <stdio.h> #define SIZE 4 struct student_type { char name[10]; int num; int age; char addr[15]; }stud[SIZE]; main( ) { int i; FILE*fp; fp=fopen("stu-list","rb"); for(i=0;i<SIZE;i++) { fread(&stud[i],sizeof(struct student_type),1,fp); printf("%\-10s %4d %4d %\-15s\n",stud[i].name,stud[i].num,stud[i]. age,stud[i].addr); } fclose (fp); }
如果已有的數(shù)據(jù)已經(jīng)以二進(jìn)制形式存儲在一個(gè)磁盤文件“stu-dat”中,要求從其中讀入數(shù)據(jù)并輸出到“stu-list”文件中,可以編寫一個(gè)load函數(shù),從磁盤文件中讀二進(jìn)制數(shù)據(jù)。 void load( ) { FILE *fp;int i; if((fp=fopen("stu-dat","rb"))==NULL) { printf("cannot open infile\n"); return; } for(i=0;i<SIZE;i++) if(fread(&stud[i],sizeof(struct student_type),1,fp)!=1) { if(feof(fp)) {fclose(fp); return;} printf("file read error\n"); } fclose (fp); } 三、格式化讀寫函數(shù)(fprintf()和fscanf()) 函數(shù)調(diào)用: fprintf ( 文件指針,格式字符串,輸出表列); fscanf ( 文件指針,格式字符串,輸入表列); 函數(shù)功能: 從磁盤文件中讀入或輸出字符。 例: fprintf(fp,”%d,%6.2f”,i,t); Fscanf (fp,”%d,%f”,&i,&t); 注意: 用fprintf和fscanf函數(shù)對磁盤文件讀寫,使用方便,容易理解,但由于在輸入時(shí)要將ASCII碼轉(zhuǎn)換為二進(jìn)制形式,在輸出時(shí)又要將二進(jìn)制形式轉(zhuǎn)換成字符,花費(fèi)時(shí)間比較多。因此,在內(nèi)存與磁盤頻繁交換數(shù)據(jù)的情況下,最好不用fprintf和fscanf函數(shù),而用fread和fwrite函數(shù)。 四、其他讀寫函數(shù) putw()和getw() 函數(shù)調(diào)用: putw(int i,FILE * fp); int i = getw(FILE * fp); 函數(shù)功能: 對磁盤文件中讀寫一個(gè)字(整數(shù))。 例: putw(10,fp); i = getw(fp); gutw函數(shù)定義如下: gutw(FILE *fp) { char s; s=char *&i; s[0] = getc(fp); s[1] = getc(fp); return i; } 用戶自定義讀取其他類型數(shù)據(jù)的函數(shù)。 向磁盤文件寫一個(gè)實(shí)數(shù)(用二進(jìn)制方式)的函數(shù)putfloat : putfloat(float num,FILE *fp) { char *s; int count; s = (char*)# for(count = 0;count < 4;count++) putc(s[count],fp); } fgets函數(shù) 函數(shù)作用: 從指定文件讀入一個(gè)字符串。 函數(shù)調(diào)用: fgets(str,n,fp); 從fp指向的文件輸入n-1個(gè)字符,在最后加一個(gè)’\0’。 返回值: str的首地址。 fputs函數(shù) 函數(shù)作用: 向指定的文件輸出一個(gè)字符串。 函數(shù)調(diào)用: fgets(“china”,fp); 第一個(gè)參數(shù)可以是字符串常量、字符數(shù)組名或字符型指針。字符串末尾的′\0′不輸出。 返回值: 輸入成功,返回值為0; 輸入失敗,返回EOF。 13.5 文件的定位 #include<stdio.h> main() { FILE *fp1,*fp2; fp1=fopen("file1.c","r"); fp2=fopen("file2.c","w"); while(!feof(fp1)) putchar(getc(fp1)); rewind(fp1); while(!feof(fp1)) putc(getc(fp1),fp2); fclose(fp1);fclose(fp2); } 順序讀寫和隨機(jī)讀寫 順序讀寫: 位置指針按字節(jié)位置順序移動(dòng)。 隨機(jī)讀寫: 讀寫完上一個(gè)字符(字節(jié))后,并不一定要讀寫其后續(xù)的字符(字節(jié)),而可以讀些文件中任意位置上所需要的字符(字節(jié))。 fseek函數(shù)(一般用于二進(jìn)制文件) 函數(shù)功能: 改變文件的位置指針。 函數(shù)調(diào)用形式: fseek(文件類型指針,位移量,起始點(diǎn)) 起始點(diǎn):文件開頭 SEEK_SET 0 文件當(dāng)前位置 SEEK_CUR 1 文件末尾 SEEK_END 2 位移量:以起始點(diǎn)為基點(diǎn),向前移動(dòng)的字節(jié)數(shù)。一般 要求為long型。 fseek函數(shù)應(yīng)用舉例 fseek(fp,100L,0);//將位置指針移到離文件頭100個(gè)字節(jié)處。 fseek(fp,50L, 1);//將位置指針移到離當(dāng)前位置50個(gè)字節(jié)處。 fseek(fp,50L, 2);//將位置指針從文件末尾處向后退10個(gè)字節(jié)。 #include <stdlib.h> #include<stdio.h> struct student_type { char name[10]; int num; int age; char sex; }stud[10]; main() { int i; FILE *fp; if((fp=fopen("stud-dat","rb"))==NULL) { printf("can not open file\n"); exit(0); } for(i=0;i<10;i+=2) { fseek(fp,i*sizeof(struct student_type),0); fread(&stud[i], sizeof(struct student_type),1,fp); printf(“%s %d %d %c\n”,stud[i].name,stud[i].num,stud[i].age,stud[i].sex); } fclose(fp); } ftell函數(shù) 函數(shù)作用: 得到流式文件中的當(dāng)前位置,用相對于文件開頭的位移量來表示。 返回值: 返回當(dāng)前位置,出錯(cuò)時(shí)返回-1L。 應(yīng)用舉例: i = ftell(fp); if(i==-1L) printf(“error\n”); 13.6 出錯(cuò)的檢測 ferror函數(shù) 調(diào)用形式: ferror(fp); 返回值: 返回0,表示未出錯(cuò);返回非0,表示出錯(cuò)。 在調(diào)用一個(gè)輸入輸出函數(shù)后立即檢查ferror函數(shù)的值,否則信息會丟失。在執(zhí)行fopen函數(shù)時(shí),ferror函數(shù)的初始值自動(dòng)置為0。 clearerr函數(shù) 調(diào)用形式: clearerr(fp); 函數(shù)作用: 使文件錯(cuò)誤標(biāo)志和文件結(jié)束標(biāo)志置為0。 只要出現(xiàn)錯(cuò)誤標(biāo)志,就一直保留,直到對同一文件調(diào)用clearerr函數(shù)或rewind函數(shù),或任何其他一個(gè)輸入輸出函數(shù)。 13.7 文件輸入輸出小結(jié)
|
|