第十章:对文件的输入输出

第十章:对文件的输入输出

10.1 C文件的有关基本知识

10.1.1 什么是文件

文件有不同的类型,在程序设计中,主要用到两种文件:
(1)程序文件。包括源程序文件(后缀为.c)、目标文件(后缀为.obj)、可执行文件(后缀为.exe)等。这种文件的内容是程序代码。
(2)数据文件。文件的内容不是程序,而是供程序运行时读写的数据,如在程序运行过程中输出到磁盘(或其他外部设备)的数据,或在程序运行过程中供读入的数据。如一批学生的成绩数据、货物交易的数据等。
本章主要讨论的是数据文件
在以前各章中所处理的数据的输入和输出,都是以终端为对象的,即从终端的键盘输入数据,运行结果输出到终端显示器上。实际上,常常需要将一些数据(运行的最终结果或中间数据)输出到磁盘上保存起来,以后需要时再从磁盘中输入到计算机内存。这就要用到磁盘文件
为了简化用户对输入输出设备的操作,使用户不必去区分各种输入输出设备之间的区别,操作系统把各种设备都统一作为文件来处理。从操作系统的角度看,每一个与主机相连的输人输出设备都看作一个文件。例如,终端键盘是输入文件,显示屏和打印机是输出文件。
文件(file)是程序设计中一个重要的概念。所谓“文件”一般指存储在外部介质上数据的集合。一批数据是以文件的形式存放在外部介质(如磁盘)上的。操作系统是以文件为单位对数据进行管理的,也就是说,如果想找存放在外部介质上的数据,必须先按文件名找到所指定的文件,然后再从该文件中读取数据。要向外部介质上存储数据也必须先建立一个文件(以文件名作为标志),才能向它输出数据。
输入输出是数据传送的过程,数据如流水一样从一处流向另一处,因此常将输入输出形象地称为(stream),即数据流。流表示了信息从源到目的端的流动。在输入操作时,数据从文件流向计算机内存,在输出操作时,数据从计算机流向文件(如打印机,磁盘文件)。文件是由操作系统进行统一管理的,无论是用Word打开或保存文件,还是C程序中的输入输出都是通过操作系统进行的。“流”是一个传输通道,数据可以从运行环境(有关设备)流入程序中,或从程序流至运行环境。
C语言把文件看作一个字符(或字节)的序列,即由一个一个字符(或字节)的数据顺序组成。一个输人输出流就是一个字符流或字节(内容为二进制数据)流。
C的数据文件由一连串的字符(或字节)组成,而不考虑行的界限,两行数据间不会自动加分隔符,对文件的存取是以字符(字节)为单位的。输入输出数据流的开始和结束仅受程序控制而不受物理符号(如回车换行符)控制,这就增加了处理的灵活性。这种文件称为流式文件。

10.1.2 文件名

一个文件要有一个唯一的文件标识,以便用户识别和引用。文件标识包括3部分:(1)文件路径;(2)文件名主干;(3)文件后缀。
文件路径表示文件在外部存储设备中的位置。如:
在这里插入图片描述
表示filel.dat文件存放在D盘中的CC目录下的temp子目录下面。
为方便起见,文件标识常被称为文件名,但应了解此时所称的文件名,实际上包括以上3部分内容,而不仅是文件名主干。文件名主干的命名规则遵循标识符的命名规则。后缀用来表示文件的性质,如:doc(Word 生成的文件),txt(文本文件),dat(数据文件),c(C语言源程序文件),cpp(C++源程序文件),for(FORTRAN语言源程序文件),pas(Pascal语言源程序文件),obj(目标文件),exe(可执行文件).ppt(电子幻灯文件),bmp(图形文件)等。

10.1.3文件的分类

根据数据的组织形式,数据文件可分为ASCII文件二进制文件。数据在内存中是以二进制形式存储的,如果不加转换地输出到外存,就是二进制文件,可以认为它就是存储在内存的数据的映像,所以也称之为映像文件(image file)。如果要求在外存上以ASCII代码形式存储,则需要在存储前进行转换。ASCII文件又称文本文件(text file),每一个字节存放一个字符的ASCII代码。
一个数据在磁盘上怎样存储呢?字符一律以ASCII 形式存储,数值型数据既可以用ASCII形式存储,也可以用二进制形式存储。如有整数10000,如果用ASCII码形式输出到磁盘,则在磁盘中占5个字节(每一个字符占一个字节),而用二进制形式输出,则在磁盘上只占4个字节(用Visual C++时),见图10.1。

第十章:对文件的输入输出_第1张图片
用ASCII码形式输出时字节与字符一一对应,一个字节代表一个字符,因而便于对字符进行逐个处理,也便于输出字符。但一般占存储空间较多,而且要花费转换时间(二进制形式与ASCII码间的转换)。用二进制形式输出数值,可以节省外存空间和转换时间,把内存中的存储单元中的内容原封不动地输出到磁盘(或其他外部介质)上,此时每一个字节并不一定代表一个字符。如果程序运行过程中有的中间数据需要保存在外部介质上,以便在需要时再输入到内存,一般用二进制文件比较方便。在事务管理中,常有大批数据存放在磁盘上,随时调入计算机进行查询或处理,然后又把修改过的信息再存回磁盘,这时也常用二进制文件。

10.1.4 文件缓冲区

ANSI C标准采用“缓冲文件系统”处理数据文件,所谓缓冲文件系统是指系统自动地在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区。从内存向磁盘输出数据必须先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘去。如果从磁盘向计算机读入数据,则一次从磁盘文件将一批数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(给程序变量),见图10.2。这样做是为了节省存取时间,提高效率,缓冲区的大小由各个具体的C编译系统确定。
说明:每一个文件在内存中只有一个缓冲区,在向文件输出数据时,它就作为输出缓冲区,在从文件输入数据时,它就作为输入缓冲区。
第十章:对文件的输入输出_第2张图片

10.1.5文件类型指针

缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。每个被使用的文件都在内存中开辟一个相应的文件信息区,用来存放文件的有关信息(如文件的名字、文件状态及文件当前位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名为FILE。例如有一种C编译环境提供的stdio. h头文件中有以下的文件类型声明

typedef struct
	{short level;				//缓冲区“满”或“空”的程度
	 unsigned flags;			//文件状态标志
	 char fd;					//文件描述符
	 unsigned char hold;		//如缓冲区无内容不读取字符
	 short bsize ;				//缓冲区的大小
	 unsigned char * buffer;	//数据缓冲区的位置
	 unsigned char * curp;		//文件位置标记指针当前的指向
	 unsigned istemp;			//临时文件指示器
	 short token;				//用于有效性检查
	}FILE;

不同的C编译系统的FILE类型包含的内容不完全相同,但大同小异。对以上结构体中的成员及其含义可不深究,只须知道其中存放文件的有关信息即可。可以看到:FILE是以上结构体类型的自己命名的类型名称,FILE与上面的结构体类型等价。
以上声明FILE结构体类型的信息包含在头文件“stdio.h”中。在程序中可以直接用FILE类型名定义变量。每一个FILE类型变量对应一个文件的信息区。在其中存放该文件的有关信息。例如。可以定义以下FILE类型的变量:

FILE f1;

以上定义了一个结构体变量f1,用它来存放一个文件的有关信息。这些信息是在打开一个文件时由系统根据文件的情况自动放入的,在读写文件时需要用到这些信息,也会修改某些信息。例如在读一个字符后,文件信息区中的位置标记指针的指向就要改变。
一般不定义FILE类型的变量命名,也就是不通过变量的名字来引用这些变量,而是设置一个指向FILE类型变量的指针变量,然后通过它来引用这些FILE类型变量。这样使用起来方便。
下面定义一个指向文件型数据的指针变量:

FILE *fp;

定义fp是一个指向FILE类型数据的指针变量。可以使fp指向某一个文件的文件信息区(是一个结构体变量),通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件。如果有n个文件,应设n个指针变量,分别指向n个FILE类型变量,以实现对n个文件的访问,见图10.3。
第十章:对文件的输入输出_第3张图片
为方便起见,通常将这种指向文件信息区的指针变量简称为指向文件的指针变量
注意:指向文件的指针变量并不是指向外部介质上的数据文件的开头,而是指向内存中的文件信息区的开头。

10.2 打开与关闭文件

对文件读写之前应该“打开”该文件,在使用结束之后应“关闭”该文件。“打开”和“关闭”是形象的说法,好像打开门才能进入房子,门关闭就无法进入一样。实际上,所谓“打开”是指为文件建立相应的信息区(用来存放有关文件的信息)和文件缓冲区(用来暂时存放输人输出的数据)。
在编写程序时,在打开文件的同时,一般都指定一个指针变量指向该文件,也就是建立起指针变量与文件之间的联系,这样,就可以通过该指针变量对文件进行读写了。所谓“关闭”是指撤销文件信息区和文件缓冲区,使文件指针变量不再指向该文件,显然就无法进行对文件的读写了。

10.2.1 用fopen函数打开数据文件

ANSI C规定了用标准输入输出函数fopen来实现打开文件。fopen函数的调用方式为
fopen(文件名,使用文件方式);
例如:

fopen("a1" ,"r");

表示要打开名字为a1的文件,使用文件方式为“读入”(r代表read,即读入)。fopen函数的返回值是指向a1文件的指针(即al文件信息区的起始地址)。通常将fopen函数的返回值赋给一个指向文件的指针变量。如:

FILE* fp;			//定义一个指向文件的指针变量fp
fp= fopen("a1","r");//将 fopen函数的返回值赋给指针变量fp

这样fp就和文件a1相联系了,或者说,fp指向了a1文件。可以看出,在打开一个文件时,通知编译系统以下3个信息:①需要打开文件的名字,也就是准备访问的文件的名字;②使用文件的方式(“读”还是“写”等);③让哪一个指针变量指向被打开的文件。
使用文件方式见表10.1。
第十章:对文件的输入输出_第4张图片
(1)用r方式打开的文件只能用于向计算机输入而不能用作向该文件输出数据,而且该文件应该已经存在,并存有数据,这样程序才能从文件中读数据。不能用r方式打开一个并不存在的文件,否则出错。
(2)用w方式打开的文件只能用于向该文件写数据(即输出文件),而不能用来向计算机输人。如果原来不存在该文件,则在打开文件前新建立一个以指定的名字命名的文件。如果原来已存在一个以该文件名命名的文件,则在打开文件前先将该文件删去,然后重新建立一个新文件。
(3)如果希望向文件末尾添加新的数据(不希望删除原有数据),则应该用a方式打开。但此时应保证该文件已存在;否则将得到出错信息。打开文件时,文件读写位置标记移到文件末尾。
(4)用“r+”"w+”"a+”方式打开的文件既可用来输入数据,也可用来输出数据。用“r+”方式时该文件应该已经存在,以便计算机从中读数据。用“w+”方式则新建立一个文件,先向此文件写数据,然后可以读此文件中的数据。用"a+”方式打开的文件,原来的文件不被删去,文件读写位置标记移到文件末尾,可以添加,也可以读。
(5)如果不能实现“打开”的任务,fopen函数将会带回一个出错信息。出错的原因可能是:用r方式打开一个并不存在的文件;磁盘出故障;磁盘已满无法建立新文件等。此时fopen函数将带回一个空指针值NULL(在stdio.h头文件中,NULL已被定义为0)。
常用下面的方法打开一个文件:

if((fp=open("file1","r"))==NULL)
	{printf("cannot open this file\n");
	 exit(0);
	}

即先检查打开文件的操作有否出错,如果有错就在终端上输出 cannot open this file。exit函数的作用是关闭所有文件,终止正在执行的程序,待用户检查出错误,修改后重新运行。
(6)C标准建议用表10.1列出的文件使用方式打开文本文件或二进制文件,但目前使用的有些C编译系统可能不完全提供所有这些功能(例如,有的只能用r,w,a方式),有的C版本不用“r+”“w+”“a+”,而用rw,wr,ar等,请读者注意所用系统的规定。
(7)在表10.1中,有12种文件使用方式,其中有6种是在第一个字母后面加了字母b的(如 rb,wb,ab,rb+,wb+,ab+),b表示二进制方式。其实,带b和不带b只有一个区别,即对换行的处理。由于在C语言用一个’\n’即可实现换行,而在 Windows系统中为实现换行必须要用“回车”和“换行”两个字符,即’\r’和’\n’。因此,如果使用的是文本文件并且用w方式打开,在向文件输出时,遇到换行符\n’时,系统就把它转换为\r’和’\n’两个字符,否则在 Windows系统中查看文件时,各行连成一片,无法阅读。同样,如果有文本文件且用r方式打开,从文件读入时,遇到’\r’和’\n’两个连续的字符,就把它们转换为’\n’一个字符。如果使用的是二进制文件,在向文件读写时,不需要这种转换。加 b表示使用的是二进制文件,系统就不进行转换。
(8)如果用wb的文件使用方式,并不意味着在文件输出时把内存中按ASCII形式保存的数据自动转换成二进制形式存储。输出的数据形式是由程序中采用什么读写语句决定的。例如,用fscanf和 fprintf函数是按ASCII方式进行输入输出,而fread和 fwrite函数是按二进制进行输入输出。各种对文件的输入输出语句,详见10.3节。
在打开一个输出文件时,是选w还是wb方式,完全根据需要,如果需要对回车符进行转换的,就用w,如果不需要转换的,就用wb。带b只是通知编译系统:不必进行回车符的转换。如果是文本文件(例如一篇文章),显然需要转换,应该用w方式。如果是用二进制形式保存的一批数据,并不准备供人阅读,只是为了保存数据,就不必进行上述转换。可以用wb方式。一般情况下,带b的用于二进制文件,常称为二进制方式,不带b的用于文本文件,常称为文本方式,从理论上说,文本文件也可以 wb方式打开,但无必要。
(9)程序中可以使用3个标准的流文件——标准输入流标准输出流标准出错输出流。系统已对这3个文件指定了与终端的对应关系。标准输入流是从终端的输入,标准输出流是向终端的输出,标准出错输出流是当程序出错时将出错信息发送到终端。
程序开始运行时系统自动打开这3个标准流文件。因此,程序编写者不需要在程序中用fopen函数打开它们。所以以前我们用到的从终端输入或输出到终端都不需要打开终端文件。系统定义了3个文件指针变量stdin,stdout和 stderr,分别指向标准输入流、标准输出流和标准出错输出流,可以通过这3个指针变量对以上3种流进行操作,它们都以终端作为输入输出对象。例如程序中指定要从stdin所指的文件输入数据,就是指从终端键盘输入数据。

10.2.2 用fclose函数关闭数据文件

在使用完–个文件后应该关闭它,以防止它再被误用。“关闭”就是撤销文件信息区和文件缓冲区,使文件指针变量不再指向该文件,也就是文件指针变量与文件"脱钩”,此后不能再通过该指针对原来与其相联系的文件进行读写操作,除非再次打开,使该指针变量重新指向该文件。
关闭文件用fclose函数。fclose函数调用的一般形式为
fclose(文件指针);
例如:

fclose(fp);

前面曾把打开文件(用fopen函数)时函数返回的指针赋给了fp,现在把fp指向的文件关闭,此后fp不再指向该文件。
如果不关闭文件就结束程序运行将会丢失数据。因为,在向文件写数据时,是先将数据输出到缓冲区,待缓冲区充满后才正式输出给文件。如果当数据未充满缓冲区时程序结束运行,就有可能使缓冲区中的数据丢失。用fclose函数关闭文件时,先把缓冲区中的数据输出到磁盘文件、然后才撤销文件信息区。有的编译系统在程序结束前会自动先将缓冲区中的数据写到文件,从而避免了这个问题,但还是应当养成在程序终止之前关闭所有文件的习惯。
fclose函数也带回一个值,当成功地执行了关闭操作,则返回值为0;否则返回EOF(-1)。

10.3 顺序读写数据文件

10.3.1 怎样向文件读写字符

第十章:对文件的输入输出_第5张图片
说明:fgetc的第1个字母f代表文件(file),中间的get表示“获取”,最后一个字母c表示字符(character),fgetc的含义很清楚:从文件读取一个字符。fputc也类似。

例:从键盘输入一些字符,并逐个把它们送到磁盘上区,直到用户输入一个“#”为止

#include
#include
int main()
	{FILE *fp;
	 char ch,filename[10];
	 printf("请输入所用的文件名:");
	 scanf("%s",filename);
	 getchar();
	 if((fp=fopen(filename,"w"))==NULL)
	 	{
	 	 printf("cannont open file\n");
	 	 exit(0);
	 	}
	 printf("请输入一个准备好存储到磁盘的字符串(以#结束):");
	 ch==getchar();
	 while(ch!='#')
	 	{
	 	 fputc(ch,fp);
	 	 putchar(ch);
	 	 ch=getchar();
	 	}
	 fclose(fp);
	 putchar(10);
	 return 0;

例:将一个磁盘文件中的信息复制到另一个磁盘文件中。今要求将上例建立的filel.dat文件中的内容复制到另一个磁盘文件file2.dat 中。

#include
#include 
int main()
	{FILE *in, *out ;						//定义指向FILE类型文件的指针变量
	 char ch,infile[10],outfile[10];		//定义两个字符数组,分别存放两个数据文件名
	 printf("输入读入文件的名字:");
	 scanf("%s",infile);					//输入一个输入文件的名字
	 printf("输入输出文件的名字:"):
	 scanf("%s",outfile);					//输人一个输出文件的名字
	 if((in=fopen(infile,"r"))==NULL)		//打开输入文件
		{printf("无法打开此文件\n");
		 exit(0);
		}
	 if((out=fopen(outfile,"w"))==NULL)		//打开输文件
		 {printf("无法打开此文件\n");
		  exit(0);
		 }
	 ch=fgetc(in);							//从输人文件读入一个字符,赋给变量ch
	 while(!feof(in))						//如果未遇到输人文件的结束标志
		{fputc(ch,out); 					//将ch写到输出文件
		 putchar(ch);						//将ch显示到屏幕上
		 ch=fgetc(in);						//!再从输入文件读入一个字符,赋给变量ch
		}
	 putchar(10);							//显示完全部字符后换行
	 fclose(in);							//关闭输人文件
	 fclose(out);							//关闭输出文件
	 return 0;

10.3.2 怎样向文件读写一个字符串

第十章:对文件的输入输出_第6张图片
说明:
(1) fgets函数的函数原型为

char * fgets (char *str,int n,FILE *fp);

其作用是从文件读入一个字符串。调用时可以写成下面的形式:

fgets(str,n,fp);

其中,n是要求得到的字符个数,但实际上只从fp所指向的文件中读入n-1个字符,然后在最后加一个’\0’字符,这样得到的字符串共有n个字符,把它们放到字符数组str中。如果在读完n-1个字符之前遇到换行符“\n”或文件结束符EOF,读入即结束,但将所遇到的换行符“\n”也作为一个字符读入。若执行fgets函数成功,则返回值为str数组首元素的地址,如果一开始就遇到文件尾或读数据出错,则返回NULL。
(2) fputs函数的函数原型为

int fputs (char * str,FILE *fp);

其作用是将str所指向的字符串输出到fp所指向的文件中。调用时可以写成

fputs("China" ,fp);

把字符串"China"输出到fp指向的文件中。fputs函数中第一个参数可以是字符串常量、字符数组名或字符型指针。字符串末尾的’\0’不输出。若输出成功,函数值为0;失败时,函数值为EOF(即-1)。
fgets和 fgets这两个函数的功能类似于gets和 puts函数,只是 gets和 puts 以终端为读写对象,而fgets 和 fputs函数以指定的文件作为读写对象。

10.3.3 用格式化的方式读写文本文件

前面进行的是字符的输入输出,而实际上数据的类型是丰富的。大家已很熟悉用printf函数和scanf函数向终端进行格式化的输入输出,即用各种不同的格式以终端为对象输入输出数据。其实也可以对文件进行格式化输入输出,这时就要用fprintf函数和fscanf函数,从函数名可以看到,它们只是在printf和 scanf 的前面加了一个字母f。它们的作用与printf函数和scanf函数相仿,都是格式化读写函数。只有一点不同:fprintf 和 fscanf 函数的读写对象不是终端而是文件。它们的一般调用方式为
fprintf(文件指针,格式字符串,输出表列);
fscanf(文件指针,格式字符串,输入表列);

例如:

fprintf( fp,"%d,%6.2f,i,f):

它的作用是将int型变量i和 float型变量f的值按%d和%6.2f的格式输出到fp指向的文件中。若i=3,f=4.5,则输出到磁盘文件上的是以下的字符:

34.50

这是和输出到屏幕的情况相似的,只是它没有输出到屏幕而是输出到文件而已。

10.3.4 用二进制方式向文件读写一组数据

在程序中不仅需要一次输入输出一个数据,而且常常需要一次输入输出一组数据(如数组或结构体变量的值),C语言允许用fread函数从文件中读一个数据块,用fwrite函数向文件写一个数据块。在读写时是以二进制形式进行的。在向磁盘写数据时,直接将内存中一组数据原封不动、不加转换地复制到磁盘文件上,在读入时也是将磁盘文件中若于字节的内容一批读入内存。
它们的一般调用形式为

fread(buffer,size,count,fp);
fwrite(buffer,size,count,fp);

其中:
buffer:是一个地址。对 fread来说,它是用来存放从文件读入的数据的存储区的地址。对fwrite来说,是要把此地址开始的存储区中的数据向文件输出(以上指的是起始地址)。
size:要读写的字节数。
count:要读写多少个数据项(每个数据项长度为size)。fp: FILE类型指针。
在打开文件时指定用二进制文件,这样就可以用fread和fwrite函数读写任何类型的信息,例如:

fread(f,4,10,fp);

其中,f是一个float型数组名(代表数组首元素地址)。这个函数从fp所指向的文件读入10个4个字节的数据,存储到数组f中。

10.4 随机读写数据文件

随机访问不是按数据在文件中的物理位置次序进行读写,而是可以对任何位置上的数据进行访问,显然这种方法比顺序访问效率高得多。

10.4.1 文件位置标记及其定位

1.文件位置标记

前已介绍,为了对读写进行控制,系统为每个文件设置了一个文件读写位置标记(简称文件位置标记或文件标记),用来指示“接下来要读写的下一个字符的位置”。
一般情况下,在对字符文件进行顺序读写时,文件位置标记指向文件开头,这时如果对文件进行读的操作,就读第1个字符,然后文件位置标记向后移一个位置,在下一次执行读的操作时,就将位置标记指向的第2个字符读入。依此类推,遇到文件尾结束。见图10.4示意。

第十章:对文件的输入输出_第7张图片
如果是顺序写文件,则每写完一个数据后,文件位置标记顺序向后移一个位置,然后在下一次执行写操作时把数据写入位置标记所指的位置。直到把全部数据写完,此时文件位置标记在最后一个数据之后。
可以根据读写的需要,人为地移动文件位置标记的位置。文件位置标记可以向前移、口后移,移到文件头或文件尾,然后对该位置进行读写,显然这就不是顺序读写了,而是随机读写。
对流式文件既可以进行顺序读写,也可以进行随机读写。关键在于控制文件的位置标记。如果文件位置标记是按字节位置顺序移动的,就是顺序读写。如果能将文件位置标记按需要移动到任意位置,就可以实现随机读写。所谓随机读写,是指读写完上一个字符(字节)后,并不一定要读写其后续的字符(字节),而可以读写文件中任意位置上所需要的字符(字节)。即对文件读写数据的顺序和数据在文件中的物理顺序一般是不一致的。可以在任何位置写入数据,在任何位置读取数据。

2 文件位置标记的定位

可以强制使文件位置标记指向人们指定的位置。可以用以下函数实现。
(1)用rewind函数使文件位置标记指向文件开头
rewind 函数的作用是使文件位置标记重新返回文件的开头,此函数没有返回值。

例:有一个磁盘文件,内有一些信息。要求第1次将它的内容显示在屏幕上,第2次把它复制到另一文件上。

#include
int main()
	{FILE *fp1,*fp2;
	 fpl=fopen("file1.dat","r");	//打开输入文件
	 fp2=fopen("file2.dat","w");	//打开输出文件
	 ch=getc(fpl);					//从file1.dat文件读入第一个字符
	 while(! feof(fpl))				//当未读取文件尾标志
		{putchar(ch);				//在屏幕输出一个字符
		 ch=getc(fpl); 				//再从filel.dat 文件读入一个字符
		}
	 putchar(10);					//在屏幕执行换行
	 rewind(fpl);					//使文件位置标记返回文件开头
	 ch= getc( fpl);				//从 file1.dat文件读入第一个字符
	 while(!feof(fpl))				//当未读取文件尾标志
		{fputc(ch,fp2);				//向file2.dat 文件输出一个字符
		 ch=fgetc(fpl);				//再从filc1.dat文件读入一个字符
		}
	 fclose(fp1);fclose(fp2);
	 return 0;
	}

rewind 函数的作用是:使文件filel的文件位置标记重新定位于文件开头,同时feof 函数的值会恢复为0(假)。
(2)用fseek函数改变文件位置标记
fseek函数的调用形式为
fseek(文件类型指针,位移量,起始点)
“起始点”用0,1或2代替,0代表“文件开始位置”,1为“当前位置”,2为“文件末尾位置”。C标准指定的名字如表10.4所示。
第十章:对文件的输入输出_第8张图片
“位移量”指以“起始点”为基点,向前移动的字节数。位移量应是 long 型数据(在数字的末尾加一个字母L,就表示是 long 型)。
fseek函数一般用于二进制文件。下面是fseek函数调用的几个例子:

fseek (fp,100L,0);//将文件位置标记向前移到离文件开头100个字节处
fseek (fp,50L,1);//将文件位置标记向前移到离当前位置50个字节处
fseek (fp,-10L,2);//将文件位置标记从文件末尾处向后退10个字节

(3)用ftell函数测定文件位置标记的当前位置
ftell函数的作用是得到流式文件中文件位置标记的当前位置。
由于文件中的文件位置标记经常移动,人们往往不容易知道其当前位置,所以常用ftell函数得到当前位置,用相对于文件开头的位移量来表示。如果调用函数时出错(如不存在fp指向的文件),ftell函数返回值为-1L。例如:

i=ftell(fp);//变量i存放文件当前位置
if(i==1L) printf("error\n");//如果调用函数时出错,输出"error"

10.4.2 随机读写

例:在磁盘文件上存有10个学生的数据。要求将第1,3,5,7,9个学生数据输入计算机,并在屏幕上显示出来。

#include
#include

struct Student_type
	{ char name[10];
	  int num;
	  int age;
	  int age;
	  char addr[15];
	}stud[10];

int main()
	{int i;
	 FILE *fp;
	 if ((fp=fopen("stu.dat","rb"))==NULL)
	 	{printf("cannot 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("%-10s %4d %d %-15s\n",stud[i].name,stud[i].num,stud[i].age,stud[i].addr);
	 fclose(fp);
	 return 0;
	}

10.5 文件读写的出错检测

1. ferror函数

在调用各种输人输出函数(如 putc,getc,fread和fwrite等)时,如果出现错误,除了函数返回值有所反映外,还可以用ferror函数检查。它的一般调用形式为
ferror(fp);
如果ferror返回值为0(假),表示未出错;如果返回一个非零值,表示出错。
应该注意,对同一个文件每一次调用输入输出函数,都会产生一个新的ferror函数值,因此,应当在调用一个输入输出函数后立即检查ferror函数的值,否则信息会丢失。
在执行fopen函数时,ferror函数的初始值自动置为0。

2. clearerr函数

clearerr的作用是使文件出错标志和文件结束标志置为0。假设在调用一个输入输出函数时出现错误,ferror函数值为一个非零值。应该立即调用clearerr(fp),使ferror(fp)的值变成0,以便再进行下一次的检测。
只要出现文件读写出错标志,它就一直保留,直到对同一文件调用clearerr函数或rewind函数,或任何其他一个输入输出函数。
文件这一章的内容在实际应用中是很重要的,许多可供实际使用的C程序(尤其是有关事务管理的程序)都包含了文件处理。通常将大批数据存放在磁盘上,在运行应用程序的过程中,内存与磁盘之间频繁地交换数据,从磁盘中读入数据到计算机内存,程序对这些数据进行检查、分析、修改和其他处理,把修改过的数据再保存在磁盘上。这就牵涉到许多文件操作。本章只介绍了一些最基本的概念,并通过一些简单的例子使读者初步了解怎样进行文件操作,为今后进一步学习和应用打下必要的基础。

你可能感兴趣的:(c语言程序设计,网络,服务器,运维)