630 likes | 799 Views
C 语言程序设计. 主讲:沈济南. TEL: 13971887071 E-mail: shenjinan@163.com. 第二十讲. 主讲内容: 第十章 文件 10.1 文件的基本概念 10.2 文件类型指针 10.3 文件的打开与关闭 10.4 文件的读写操作 10.5 文件的随机读写操作 10.6 文件检测函数. 10.1 文件的基本概念. 10.1.1 文本文件与二进制文件
E N D
C语言程序设计 主讲:沈济南 TEL:13971887071 E-mail:shenjinan@163.com
第二十讲 • 主讲内容: • 第十章 文件 10.1文件的基本概念 10.2文件类型指针 10.3文件的打开与关闭 10.4文件的读写操作 10.5文件的随机读写操作 10.6文件检测函数
10.1文件的基本概念 • 10.1.1文本文件与二进制文件 C语言中文件并不是由记录(record)组成的。根据数据的组织形式,可分为ASCII文件和二进制文件。ASCII文件又称文本(text)文件,它的每一个字节放一个ASCII代码,代表一个字符。二进制文件是把内存中的数据按其在内存中的存储形式原样输出到磁盘上存放。
10.1.2缓冲文件系统和非缓冲文件系统 • 目前C语言所使用的磁盘文件系统有两大类:一类称为缓冲文件系统,又称为标准文件系统;另一类称为非缓冲文件系统。 • 缓冲文件系统的特点是:系统自动地在内存区为每一个正在使用的文件开辟一个缓冲区。 • 非缓冲文件系统不由系统自动设置缓冲区,而由用户自己根据需要设置。
10.2文件类型指针 • 使用文件必须包含库头文件stdio.h,且要先定义。文件定义的一般格式: • FILE 文件指针; • FILE 文件缓冲区的类型名,必须大写。 • 文件指针 指向文件缓冲区的指针。 • 例如,FILE *fp;
10.3文件的打开与关闭 • 文件处理必须包含三个基本过程:打开文件,读或写,关闭文件。
10.3.1文件的打开 • 所谓“打开”是在程序和操作系统之间建立起联系,程序把所要操作的文件的一些信息通知给操作系统。 • 文件的打开通过Fopen函数实现。它的调用方式为: • FILE *fp; • fp=fopen(文件名,使用文件方式); • 文件名中可以包含路径,例如: • fp=fopen(" d:\a1.txt", "r")
⑴ 在打开一个文件时,通常要通知给编译系统以下三个信息: • ⑵ 需要打开的文件名,也就是准备访问的文件的名字; • ⑶ 使用文件的方式(读还是写等); • ⑷ 让哪一个指针变量指向被打开的文件。
10.3.1文件的打开 • 对于文件使用方式有以下几点说明: • (1)文件使用方式字符的含意 r(read) 读 w(write) 写 a(append) 添加 t(text) 文本文件,可省略不写 b(binary) 二进制文件 + 读和写
(2) 用“r”方式打开的文件只能用于向计算机输入而不能用作向该文件输出数据,而且该文件应该已经存在,不能打开一个并不存在的用于“r”方式的文件(即输入文件),否则出错。 • (3) 用“w”方式打开的文件只能用于向该文件写数据,而不能用来向计算机输入。如果原来不存在该文件,则在打开时新建立一个以指定名字命名的文件;如果原来已存在一个以该文件名命名的文件,则在打开时将该文件删去 ,然后重新建立一个新文件。
(4)如果希望向文件末尾添加新的数据(不希望删除原有数据),则应该用“a”方式打开。但此时该文件必须已存在,否则将得到出错信息。打开时,位置指针移到文件末尾。(4)如果希望向文件末尾添加新的数据(不希望删除原有数据),则应该用“a”方式打开。但此时该文件必须已存在,否则将得到出错信息。打开时,位置指针移到文件末尾。 • (5) 用“r+”、“w+”、“a+”方式打开的文件可以用来输入和输出数据。用“r+”方式时该文件已经存在,以便能向计算机输入数据。“w+”方式则新建立一个文件,先向此文件写数据,然后可以读此文件中的数据。用“a+”方式打开的文件,原来的文件不被删去,位置指针移到文件末尾,可以添加也可以读。
(6) 如果不能实现“打开”的任务,fopen函数将会带回一个出错信息。此时fopen函数将带回一个空指针值NULL(NULL在stdio.h文件中已被定义0)。常用下面方法打开一个文件: if ((fp=fopen("file1", "r"))= =NULL) { printf("cannot open this file\n"); exit (1); } • 即先检查打开有否出错,如果有错就在终端上输出"cannot open this file"。exit函数的作用是关闭所有文件,终止正调用的过程。
10.3.2文件的关闭 • 文件使用完毕后必须关闭,以避免数据丢失。用fclose函数关闭文件。fclose函数调用的一般形式为: • fclose(文件指针); • 例如: • fclose (fp); • fclose函数也带回一个值,当顺利地执行了关闭操作,则返回值为0;如果返回值为非零值,则表示关闭时有错误。可以用ferror函数来测试。
10.4文件的读写操作 • 常用的读写函数如下,这些函数的说明包含在头文件stdio.h中: • 字符读写函数: fgetc和fputc(getc和putc); • 字符串读写函数:fgets和fputs; • 数据块读写函数:fread和fwrite; • 格式化读写函数:fscanf和fprintf。
10.4.1字符读写函数 • 1.写一个字符到磁盘文件 • fputc函数把一个字符写到磁盘文件上去。其调用形式为: • fputc (ch,fp); • 其中ch是要输出的字符,它可以是一个字符常量,也可以是一个字符变量,fp是文件指针。fputc(ch,fp)函数的作用是将字符(ch的值)输出到fp所指向的文件上去。fputc函数也带回一个值:如果输出成功则返回值就是输出字符;如果输出失败,则返回一个EOF。EOF是在stdio.h文件中定义的符号常量,值为-1。
使用fputc函数时应注意,所操作的文件必须以写、读写或添加方式打开,另外,每写入一个字符后,文件内部的位置指针自动指向下一个字节。使用fputc函数时应注意,所操作的文件必须以写、读写或添加方式打开,另外,每写入一个字符后,文件内部的位置指针自动指向下一个字节。
【例10.1】从键盘输入一行字符,写入到文本文件string.txt中。【例10.1】从键盘输入一行字符,写入到文本文件string.txt中。 • /*源程序名:CH1001.C*/ • /*01*/ #include <stdio.h> • /*02*/ void main() • /*03*/ { • /*04*/ FILE *fp; • /*05*/ char ch; • /*06*/ if((fp=fopen("c:\\string.txt","w"))==NULL)/*以写方式打开string.txt文件*/ • /*07*/ {
/*08*/ printf("can’t open file,press any key to exit!"); • /*09*/ getchar(); • /*10*/ exit(0); • /*11*/ } • /*12*/ do { • /*13*/ ch=getchar(); /*不断接收字符并写入文件,直至遇到换行符为止*/ • /*14*/ fputc(ch,fp); • /*15*/ } while (ch!='\n'); • /*16*/ fclose(fp); • /*17*/ } • 语句while中的’\n’是一个换行符。程序运行结束后可到DOS或Windows下查看刚建立的文件string.txt。
10.4.1字符读写函数 • 2.从磁盘文件中读入一个字符 • fgetc函数能够从指定文件中读入一个字符, 其调用形式为: • ch=fgetc(fp); • fp为文件指针,ch为字符变量,fget函数带回一个字符,赋给ch。如果在执行fgetc读字符时遇到文件结束符,函数返回一个文件结束标志EOF,EOF在stdio.h中定义为-1。
【例10.2】将磁盘上一个文本文件的内容复制到另一个文件中。【例10.2】将磁盘上一个文本文件的内容复制到另一个文件中。 • /*源程序名:CH1002.C*/ • /*01*/ #include <stdio.h> • /*02*/ #include <stdlib.h> • /*02*/ void main() • /*03*/ { • /*04*/ FILE *fp_in,*fp_out; • /*05*/ char infile[20], outfile[20]; • /*06*/ printf("Enter the infile name:\n"); • /*07*/ scanf("%s",infile);
/*08*/ printf("Enter the outfile name:\n"); • /*09*/ scanf("%s",outfile); • /*10*/ if ((fp_in=fopen(infile,"r"))==NULL) • /*11*/ { • /*12*/ printf("Can't open file %s\n", infile); • /*13*/ getchar(); • /*14*/ exit(0); • /*15*/ } • /*16*/ if ((fp_out=fopen(outfile,"w"))==NULL) • /*17*/ { • /*18*/ printf("can't open file %s\n", outfile); • /*19*/ getchar();
/*20*/ exit(0); • /*21*/ } • /*22*/ while(!feof(fp_in)) • /*23*/ fputc(fgetc(fp_in), fp_out); • /*24*/ fclose(fp_in); • /*25*/ fclose(fp_out); • /*26*/ } • 以上程序是按文本文件方式处理的。也可以用此程序来复制一个二进制文件,只需将两个fopen函数的“r”和“w”分别改为“rb”和“wb”即可,此时,上述程序就相当于一条copy命令了。
10.4.2字符串读写函数 • 1.从磁盘文件中读入一个字符串 • fgets的作用是从指定文件中读入一个字符串。如: • fgets (str,n,fp); • 从fp指向的文件输入n-1个字符,并把它们放到以str为起始地址的单元中。如果在读入n-1个字符结束之前遇到换行符或EOF,读入即结束。字符串读入后最后加一个’\0’字符,fgets函数返回值为str的首地址,若已到文件尾或出错,则返回NULL。
10.4.2字符串读写函数 • 2.写一个字符串到磁盘文件 • fputs函数的作用是向指定的文件输出一个字符串。如: • fputs ("China",fp); • 把字符串”China”输出到fp指向的文件。fputs函数中第一个参数可以是字符串常量、字符数组名或字符型指针。输出成功,函数值为0;失败时,为非零值。
【例10.3】编制一个将文本文件中全部信息显示到屏幕上的程序。实际上它相当于DOS系统中的type命令。【例10.3】编制一个将文本文件中全部信息显示到屏幕上的程序。实际上它相当于DOS系统中的type命令。 /*源程序名:CH1003.C*/ /*01*/ #include <stdio.h> /*02*/ #include <stdlib.h> /*03*/ void main(int argc, char* argv[ ]) /*04*/ { /*05*/ FILE *fp; /*06*/ char string[81]; /*07*/ if (argc!=2 || (fp=fopen(argv[1],"r"))==NULL)
/*08*/ { /*09*/ printf("can't open file"); /*10*/ exit(1); /*11*/ } /*12*/ while (fgets(string,81,fp)!=NULL) /*13*/ printf("%s",string); /*14*/ fclose(fp); /*15*/ }
【例10.4】在文本文件string.txt的末尾添加若干行字符。 /*源程序名:CH1004.C*/ /*01*/ #include <stdio.h> /*02*/ #include <stdlib.h> /*03*/ #include<string.h> /*04*/ void main() /*05*/ { /*06*/ FILE *fp; /*07*/ char s[81]; /*08*/ if((fp=fopen("c:\\string.txt","a"))==NULL) /*以添加方式打开string文件*/
/*09*/ { /*10*/ printf("cannot open file,press any key to exit!"); /*11*/ getchar(); /*12*/ exit(1); /*13*/ } /*14*/ while (strlen(gets(s))>0) /*从键盘读入一个字符串,遇空行则停止*/ /*15*/ { /*16*/ fputs(s,fp); /*写进指定文件*/ /*17*/ fputs("\n",fp); /*补一个换行符*/ /*18*/ } /*19*/ fclose(fp); /*20*/ }
10.4.3数据块读写函数 • 如果要一次读入一组数据(如一个数组元素、一个结构体变量的值等),则应使用fread函数和fwrite函数。数据块读写函数的调用形式为: • fread (buffer,size,count,fp); • fwrite (buffer,size,count,fp); • 其含义如下: buffer:是一个指针。对fread来说,它用于存放读入数据的首地址。对fwrite来说,是要输出数据的首地址。 size:一个数据块的字节数。 count:要读写的数据块块数。 fp:文件指针。
【例10.5】 从键盘输入一批学生的数据,然后把它们转存到磁盘文件stud.dat中。 • /*源程序名:CH1005.C*/ • /*01*/ #include <stdio.h> • /*02*/ #include <stdlib.h> • /*01*/ struct student • /*02*/ { • /*03*/ int num; • /*04*/ char name[20]; • /*05*/ char sex; • /*06*/ int age; • /*07*/ float score;
/*08*/ }; • /*09*/ void main() • /*10*/ { • /*11*/ struct student stud; • /*12*/ char numstr[20],ch; • /*13*/ FILE *fp; • /*14*/ if ((fp=fopen("stud.dat","wb"))==NULL) • /*15*/ { • /*16*/ printf("can't open file stud.dat\n"); • /*17*/ exit(1); • /*18*/ } • /*19*/ do • /*20*/ {
/*21*/ printf("enter number:");gets(numstr);stud.num =atoi(numstr); • /*22*/ printf("enter name:");gets(stud.name); • /*23*/ printf("enter sex:");stud.sex=getchar();getchar(); • /*24*/ printf("enter age:");gets(numstr);stud.age =atoi(numstr); • /*25*/ printf("enter score:");gets(numstr);stud.score =atof(numstr); • /*26*/ fwrite(&stud,sizeof(struct student),1,fp); /*将结构体变量stud的值写入文件*/ • /*27*/ printf("have another student record(y/n)?"); • /*28*/ ch=getchar();getchar(); • /*29*/ }while (ch=='Y' || ch=='y'); • /*30*/ fclose(fp); • /*31*/ }
本程序中,变量numstr从键盘接收字符串,通过atoi、atof函数进行类型转换后送到有关的结构体成员中。个别地方多出的getchar()语句,用于冲抵行末的回车符,以便于接下去的gets语句正确读取。程序运行情况如下:本程序中,变量numstr从键盘接收字符串,通过atoi、atof函数进行类型转换后送到有关的结构体成员中。个别地方多出的getchar()语句,用于冲抵行末的回车符,以便于接下去的gets语句正确读取。程序运行情况如下: • enter number:11301↙ • enter name:Zhang Ping↙ • enter sex:F↙ • enter age:19↙ • enter score:496.5↙ • have another student record(y/n)?y↙ • enter number:11302↙ • enter name:Wang Li↙ • enter sex:F↙ • enter age:20↙ • enter score:483↙ • have another student record(y/n)?n↙
10.4.4格式化读写函数 • 格式化读写函数fprintf、fscanf与函数printf、scanf的作用相仿,区别在于,fprintf和fscanf函数的读写对象不是终端而是磁盘文件。一般调用方式为: • fprintf(文件指针,格式字符串,输出表列); • fscanf(文件指针,格式字符串,输入表列);
例如: • fprintf (fp,"%d,%6.2f",a,b); • 它的作用是将整型变量a和实型变量b的值按%d和%6.2f的格式输出到fp指向的文件上。如果a=7,b=8.9,则输出到磁盘文件上的是以下的字符串: • 7,8.90 • 同样,用以下fscanf函数可以从磁盘文件上读入ASCII字符: • fscanf (fp,"%d,%f",&a,&b); • 磁盘文件上如果有以下字符: • 7,8.90 • 则将磁盘文件中的数据7送给变量a,8.90送给变量b。
10.5文件的随机读写操作 • 实际问题中有时要求从指定位置开始,也就是随机读写,这就要用到文件的位置指针。文件的位置指针指出了文件下一步的读写位置,每读写一次后,指针自动指向下一个新的位置。程序员可以通过文件位置指针移动函数的使用,实现文件的定位读写。这些函数是: • 重返文件头函数rewind; • 指针位置移动函数fseek; • 取指针当前位置函数ftell。
10.5.1重返文件头函数 • rewind函数的作用是使位置指针重新返回文件的开头。此函数没有返回值。 • 【例10.7】有一个磁盘文件,第一次使它显示在屏幕上,第二次把它复制到另一文件上。
/*源程序名:CH1007.C*/ /*01*/ # include <stdio.h> /*02*/ void main ( ) /*03*/ { /*04*/ FILE *fp1,*fp2; /*05*/ fp1=fopen ("file1.c","r"); /*06*/ fp2=fopen ("file2.c", "w"); /*07*/ while (!feof (fp1)) putchar (getc (fp1)); /*08*/ rewind (fp1); /*09*/ while (!feof (fp1)) putc (getc (fp1),fp2); /*10*/ fclose (fp1); fclose (fp2); /*11*/ } • 在第一次显示在屏幕以后,文件file1.c的位置指针已指到文件末尾,feof的值为非零(真)。执行rewind函数,使文件的位置指针重新定位于文件开头,并使feof函数的值恢复为0(假)。
10.5.2指针位置移动函数 • fseek函数用来移动文件内部位置指针,以便于文件的随机读写。 • fseek函数的调用形式为: fseek (文件指针,位移量,起始点) • 起始点:指以什么位置为基准进行移动。起始点用下列名称或代码表示,如表10.2所示。
fseek函数一般用于二进制文件,因为文本文件要发生字符转换,计算位置时往往会发生混乱。fseek函数一般用于二进制文件,因为文本文件要发生字符转换,计算位置时往往会发生混乱。 • 下面是fseek函数调用的几个例子: • fseek (fp,100L,0); /*将位置指针从文件头向前移动100个字节*/ • fseek (fp,50L,1); /*将位置指针从当前位置向前移动50个字节*/ • fseek (fp,-30L,1); /*将位置指针从当前位置往后移动30个字节*/ • fseek (fp,-10L,2); /*将位置指针从文件末尾处向后退10个字节*/
【例10.8】编程读出文件stud.dat中第三个学生的数据。【例10.8】编程读出文件stud.dat中第三个学生的数据。 • /*源程序名:CH1008.C*/ • /*01*/ #include <stdio.h> • /*02*/ #include <stdlib.h> • /*03*/ struct student • /*04*/ { • /*05*/ int num; • /*06*/ char name[20]; • /*07*/ char sex; • /*08*/ int age; • /*09*/ float score; • /*10*/ };
/*11*/ void main() • /*12*/ { • /*13*/ struct student stud; • /*14*/ FILE *fp; • /*15*/ int i=2; /*从文件头向后移动两步,就指向第三个学生的数据了,*/ • /*16*/ if ((fp=fopen("stud.dat","rb"))==NULL) • /*17*/ { • /*18*/ printf("can't open file stud.dat\n"); • /*19*/ exit(1); • /*20*/ } • /*21*/ fseek(fp,i*sizeof(struct student),0); • /*22*/ if (fread(&stud,sizeof(struct student),1,fp)==1) • /*23*/printf("%d,%s,%c,%d,%f\n",stud.num,stud.name,stud.sex,stud.age,stud.score); • /*24*/ fclose(fp); • /*25*/ }
10.5.3取指针当前位置函数 • ftell函数的作用是得到流式文件中的当前位置,用相对于文件开头的位移量来表示。由于文件中的位置指针经常移动,人们往往不容易辨清其当前位置。用ftell函数可以得到当前位置。如果ftell函数返回值为-1L,表示出错。例如: • i=ftell (fp); • if (i= =-1L) printf ("error\n"); • 变量i存放当前位置,如调用函数出错(如不存在此文件),则输出“error”。
10.5.4文件处理 • 1、文件复制 • 文件复制是从已存在的文件读出信息,原样写到另一文件。文件复制,两个文件名不能相同,其存储形式允许相同,也允许不同。这里只介绍一种ASCII码文件复制。将一个整型ASCII码文件fileA.txt复制到ASCII码文件fileB.txt。 • fileA.txt内容: • 10 11 12 13 14 15 • 20 21 22 23 24 25
【例10.10】文件复制 • /*源程序名:CH1010.C*/ • /*01*/ #include <stdio.h> • /*02*/ #include <stdlib.h> • /*03*/ void ftcopy(char fname1[20],char fname2[20]); /* 复制文本文件 */ • /*04*/ void fdisplay(char fname[20]); /* 输出文件 */ • /*05*/ void main() • /*06*/ { • /*07*/ char fname1[20],fname2[20]; • /*08*/ printf(" *** 运行结果 ***\n"); • /*09*/ printf("输入被复制的文件名:");
/*10*/ gets(fname1); • /*11*/ fdisplay(fname1); • /*12*/ printf("\n输入复制的文件名:"); • /*13*/ gets(fname2); • /*14*/ ftcopy(fname1,fname2); • /*15*/ fdisplay(fname2); • /*16*/ printf(”\n”); • /*17*/ } • /*18*/ void ftcopy(char fname1[20],char fname2[20]) /* 复制文本文件 */ • /*19*/ { • /*20*/ FILE *fp1,*fp2; char ch; • /*21*/ if ((fp1=fopen(fname1,"r"))==NULL) /* 打开被复制的文件 */ • /*22*/ {
/*23*/ printf("%s 不能打开!\n",fname1); exit(1); • /*24*/ } • /*25*/ if ((fp2=fopen(fname2,"w"))==NULL) /* 打开复制到的文件 */ • /*26*/ { • /*27*/ printf("%s 不能打开!\n",fname2); exit(1); • /*28*/ } • /*29*/ ch=fgetc(fp1); • /*30*/ while (ch!=EOF) • /*31*/ { • /*32*/ fputc(ch,fp2); ch=fgetc(fp1); • /*33*/ } • /*34*/ fclose(fp1); fclose(fp2); • /*35*/ }
/*36*/ void fdisplay(char fname[20]) /* 输出文件 */ • /*37*/ { • /*38*/ FILE *fp; char ch; • /*39*/ if ((fp=fopen(fname,"r"))==NULL) /* 打开被复制的文件 */ • /*40*/ { • /*41*/ printf("%s 不能打开!\n",fname); exit(1); • /*42*/ } • /*43*/ ch=fgetc(fp); • /*44*/ while (ch!=EOF) • /*45*/ { • /*46*/ putchar(ch); ch=fgetc(fp); • /*47*/ } • /*48*/ fclose(fp); • /*49*/ }
运行结果: • 输入被复制的文件名:fileA.txt • 10 11 12 13 14 15 • 20 21 22 23 24 25 • 输入复制的文件名:fileB.txt • 10 11 12 13 14 15 • 20 21 22 23 24 25