C语言程序设计——文件的输入输出

一. 什么是文件

文件(file)一般指存储在外部介质上数据的集合

如果想找存放在外部介质上的数据,必须先按文件名找到所指定的文件,然后再从该文件读取数据
要从外部介质上存储数据也必须先建立一个文件,才能向它输出数据

1. 程序文件

源程序文件(.c)、目标文件(.obj)、可执行文件(.exe)

这些文件表示程序代码

2. 数据文件

定义:文件的内容不是程序,而是供程序运行时读写的数据

例如:程序运行过程中输出到磁盘的数据,或程序运行过程中供读入的数据

3. 磁盘文件

为简化用户对输入输出设备操作,操作系统把各种设备统一作为文件来处理,从操作系统角度看,每一个与主机相连的输入输出设备都可看做一个文件

4. 数据流

输入输出是数据传送的过程,因此常将输入输出形象的称为流(stream)

在输入操作时,数据从文件流向计算机内存
输出操作时,数据从计算机流向文件

文件是由操作系统统一管理的

流是一个传输通道,数据可以从运行环境流入程序中,也可以从程序流至运行环境中

C语言把文件作为一个字符(字节)的序列,即由一个一个字符(或字节)的数据顺序组成。
一个输入输出流就是一个字符流或字节(内容为二进制流)流

二. 文件名

一个文件需要有唯一的文件标识,以便用户识别和引用。

1. 文件标识

C语言程序设计——文件的输入输出_第1张图片

  1. 文件路径

  2. 文件名主干

  3. 文件后缀

     文件后缀用来表示文件的性质
     如:
     	doc:word生成的文件
     	txt:文本文件
     	dat:数据文件
     	c:C语言源程序文件
     	cpp:C++源程序文件
     	for:fortran语言源程序文件
     	pas:pascal语言源程序文件
     	obj:目标文件
     	exe:可执行文件
     	ppt:电子幻灯片文件
     	bmp:图形文件
    

三. 文件的分类

根据数据的组织形式,数据文件可分为ASCII文件和二进制文件

1. 二进制文件

数据在内存中是以二进制形式存储的,如果不加转换的输出到外存,就是而二进制文件

可以认为它就是存储在内存的数据的映像,也称为映像文件

数值型数据既可以利用ASCII形式存储,也可以用二进制形式存储

优点:

节省外存空间和转换时间,把内存的存储单元内容原封不动的输出到磁盘上
此时一个字节并不代表一个字符

适用于:程序运行过程有中间数据需要保存在外部介质上,以便在需要时再输入到计算机内存,用二进制文件方便

2. ASCII文件(文本文件)

又叫文本文件,每一个字节存放一个字符的ASCII代码

字符一律以ASCII形式存储

优点:

ASCII码形式输出时字节与字符一一对应,一个字节代表一个字符,便于对字符进行逐个处理,也便于输出字符

缺点:

占存储空间多,花费转换时间(二进制形式与ASCII码间的转换)

四. 文件缓冲区

1. 定义

ANSI C标准采用“缓冲文件系统”处理数据文件,所谓缓冲文件系统是指系统自动地在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区,从内存向磁盘输出数据必须先送到内存中的缓冲区,装满缓冲区之后才一起送到磁盘去。

如果从磁盘向计算机读入数据,则一次从磁盘文件将一批数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(给程序变量)

2. 优点

节省存取时间,提高效率,缓冲区的大小由各个具体的C编译系统确定

3. 说明

每一个文件在内存中只有一个缓冲区,在向文件输出数据时,他就作为输出缓冲区,在从文件输入数据时,它就作为输入缓冲区
C语言程序设计——文件的输入输出_第2张图片

五. 文件类型指针

1. 定义

缓冲文件系统中,关键的概念是“文件类型指针”,简称文件指针

每个被使用的文件都在内存中开辟一个相应的文件信息区,用来存放文件的有关信息(如文件的名字、文件状态及文件当前位置)

这些信息都保存在结构体变量中的,该结构体由系统声明的,取名为FILE

2. 内容

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;

FILE是以上结构体类型的自己命名的类型名称,FILE与上面的结构体类型等价

以上声明FILE结构体类型的信息包含在头文件“stdio.h”中,在程序中可以直接用FILE类型名定义变量,每一个FILE类型变量对应一个文件的信息区,在其中存放该文件的有关信息

例如:定义一下FILE类型变量
	FILE f1;
	定义了一个结构体变量f1,用它来存放一个文件的有关信息
	这些信息是在打开一个文件时由系统根据文件的情况自动放入的
	在读写文件时需要用到这些信息,也会修改某些信息

3. 定义形式

文件类型指针变量的定义形式:FILE * 指针变量名

例如:FILE * fp1, fp2;

一般不定义FILE类型的变量命名,也就是不通过变量的名字来引用这些变量,而是设置一个指向FILE类型变量的指针变量,然后通过它来引用这些FILE类型变量,这样使用方便

定义一个指向文件型数据的指针变量:
	FILE * fp;
	定义的fp是一个指向FILE类型数据的指针变量,可以使fp指向某一个文件的文件信息区(是一个结构体)通过该文件信息区中的信息就能够访问该文件。
	也就是说,通过文件指针变量能够找到与它关联的文件

如果有n个文件,应设n个指针变量,分别指向n个FILE类型变量,以实现对n个文件的访问
为了方便起见,通常将这种文件信息区的指针变量简称:指向文件的指针变量
C语言程序设计——文件的输入输出_第3张图片

六. 打开与关闭文件

1. 打开文件定义

是指为文件建立相应的信息区(用来存放有关文件的信息)和文件缓冲区(用来暂时存放输入输出的数据)

在编写程序时,在打开文件的同时,一般都指定一个指针变量指向该文件,也就是建立起指针变量与文件之间的联系,这样就可以通过该指针变量对文件进行读写

2. 用fopen函数打开数据文件

ANSI C规定了用标准输入输出函数fopen来实现打开文件

fopen函数的调用方式:fopen(文件名/文件路径,使用文件方式)

例如:fopen("a1","r");
	表示要打开名字为a1的文件,使用文件方式为“读入”(r代表read,读入)
	当直接写文件的名字的时候,会打开编写C程序的同一文件夹
	
	fopen函数返回值是指向a1文件的指针(即a1文件信息区的起始地址)
	
通常将fopen函数的返回值赋给一个指向文件的指针变量	
例如:
	FILE * fp;				//定义一个指向文件的变量
	fp=fopen("a1","r")		//将fopen函数的返回值赋给指针变量fp
	
	这样的话fp就和文件a1相联系了,或者说fp指向了a1文件。		

C语言程序设计——文件的输入输出_第4张图片

3. 关闭文件

指撤销文件信息区和文件缓冲区,使文件指针变量不再指向该文件,显然就无法进行对文件的读写了

4. 用fclose函数关闭数据文件

fclose函数调用的一般形式:fclose(文件指针);

例如:
	FILE *fp1,*fp2;
	fp=fopen("file_a", "r");
	fclose(fp);
	
	
	前面曾把打开文件(用fopen函数)时函数返回的指针赋给了fp,现在把fp指向的文件关闭,此后fp不再指向该文件
	
	fclose函数也带回一个值,当成功地执行了关闭操作,返回值为0,否则返回值为EOF(-1)

如果不关闭文件就结束程序运行将会丢失数据。
因为在向文件写数据时,是先将数据输出到缓冲区,等待缓冲区充满后才正式输出给文件,如何当数据没有充满缓冲区就结束运行程序,可能是缓冲区中的数据丢失,

用fclose函数关闭文件时,先把缓冲区中的数据输出到磁盘文件,然后才撤销文件信息区。

有的编译系统在程序结束前会自动先将缓冲区中的数据写到文件,从而避免了这个问题

七. 顺序读写数据文件

文件打开之后就可以对文件进行读写了
在顺序写的时候,先写的数据存放在文件中前面的位置
在顺序读时,先读文件中前面的数据
顺序读写需要用库函数实现

1. 向文件读写字符

(1). 读入一个字符

函数名:fgetc
调用形式:fgetc(fp)
功能:从fp指向的文件读入一个字符
返回值:读成功,带回所读字符;读失败,返回文件结束标志EOF(-1)

题目:将磁盘文件中信息复制到另一个磁盘文件

解题思路:从file.txt文件中逐个读入字符,然后逐个输出到file2.txt中

代码举例:

#include 
#include 
int main(void)
{
	FILE *in, *out;
	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))		//如果未遇到输入文件的结束标志 
			//等价于:while(ch!=-1)或while(ch!=EOF)
	{
		fputc(ch,out);		//将ch写到输出文件 
		putchar(ch);		//将ch显示到屏幕上 
		ch=fgetc(in);		//再从输入文件读入一个字符,赋给变量ch 
	}
	putchar(10);			//显示完全部字符后换行 
	fclose(in);
	fclose(out);
	
	return 0;
}

判断文件结束feof

在访问磁盘文件时,是逐个字符(字节)进行的,为了知道当前访问到第几个字节,系统用文件读写位置标记,来表示当前所访问的位置

开始文件读写标记指向第1个字节,每访问完一个字节后,当前读写位置就指向下一个字节,即当前读写位置自动后移

为了知道对文件的读写是否完成,只需要看文件读写位置是否移到文件的末尾

使用方法:feof(文件指针)

例如:
	FILE *fp;
	feof(fp);
	如果文件结束返回1
	否则返回0

(2). 写入一个字符

函数名:fputc
调用形式:fputc(ch,fp)
功能:把字符ch写到文件指针变量fp所指向的文件中
返回值:输入成功,返回值就是输出的字符;输出失败,返回EOF(-1)

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

解题思路:用fgetc函数从键盘输入一些字符,然后用fputc函数写到磁盘

代码举例:

#include 
#include 
int main(void)
{
	FILE *fp;
	char ch,filename[10];	//文件名的字符数组
	printf("请输入所用文件的文件名:");
	scanf("%s", filename);
	getchar();			//用来消化最后输入的回车符
				//回车表示输入字符串结束,但是回车符任然保留在缓冲区中,
				//如果不去掉,会之后读入数据时,会把它作为有效数据读取
				//使用getchar函数把它读取,但是不赋值给任何变量,只是进行消化了
	if((fp=fopen(filename,"w"))==NULL)	//打开输出文件并使fp指向此文件
	{
		printf("cannot open file\n");
		exit(0);				//终止程序
	}
	printf("请输入一个准备存储到磁盘的字符串(以#结束):");
	ch=getchar();		//接受从键盘输入的第一个字符
	while(ch!='#')
	{
		fputc(ch, fp);	//向磁盘文件输出一个字符
		putchar(ch);	//将输出的字符显示在屏幕上
		ch=getchar();	//再接受从键盘输入的一个字符
	}
	fclose(fp);		//关闭文件
	putchar(10);	//向屏幕输出一个换行符
	return 0;
}

2. 向文件读写一个字符串

函数名:fgets
调用形式:fgets(str,n,fp);
函数功能:从fp指向的文件读入一个长度为(n-1)的字符串,存放到字符数组str中
返回值:读成功,返回str首地址,读失败返回NULL

为什么长度是n-1呢?
因为:字符串最后一位是'\0'

函数名:fputs
调用形式:fputs(str,fp);
函数功能:把str所指向的字符串写到文件指针变量fp所指向的文件中
返回值:输出成功,返回0,否则返回EOF

3. 用格式化的方式读写文本文件

格式化读写函数,fprintf和fscanf函数的读写对象不是终端而是文件

函数调用格式:
fprint(文件指针,格式字符串,输出列表);
fscanf(文件指针,格式字符串,输入列表);

函数功能:
从磁盘文件中读入字符

例如:
	fprintf(fp, "%d,%6.2f", i, f);
	作用:将int型变量i和float型变量f的值按%d,%6.2f的格式输出到fp指向的文件中

从磁盘文件中输出字符

例如:
	fscanf(fp, "%d, %f", &i, &f);
	作用:从磁盘文件读入ASCII字符

缺点:

由于在输入时要将文件中的ASCII吗转换为二进制形式在保存在内存变量中
在输出时又要将内存中的二进制形式转换成字符,花费时间多

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

在程序中不仅需要一次输入输出一个数据,而且常常需要一次输出一组数据(如数组或结构体变量)C语言运行试验fread函数从文件中读一个数据块,用fwrite函数向文件写一个数据块

在读写时是以二进制形式进行的
向磁盘写数据时,直接将内存中的一组数据原封不动的、不加转换的复制到磁盘文件上
读入的时候也是一样的

调用形式:
fread(buffer,size,count,fp);
fwrite(buffer,size,count,fp);

buffer:
	是一个地址,对fread来说,它用来存放从文件读入的数据的存储区的地址
	对fwrite来说,要把此地址开始的存储区中的数据向文件输出

size:
	要读写的字节数

count:
	要读写多少个数据项(每个数据项长度为size)

fp:
	FILE类型指针

八. 随机读写数据文件

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

1. 文件位置标记

为了对读写进行控制,系统为每个文件设置了一个文件读写位置标记(简称文件位置标记),用来指示读写的下一个字符的位置

C语言程序设计——文件的输入输出_第5张图片
顺序读写:文件位置标记是按字节位置顺序移动的
随机读写:文件位置标记按需要移动到任意位置

2. 文件位置标记的定位

用以下函数强制使文件位置标记指向人们指定位置

1.用rewind函数使文件位置标记指向文件开头

函数调用形式:rewind(pf);

此函数没有返回值

2.用fseek函数改变文件位置标记,接着读写操作将从此位置开始

函数调用形式:fseek(pf,offset,origin)

	offset:位移量
	origin:起始点

3.用ftell函数测定文件位置标记的当前位置

文件的当前位置,用相对于文件开头的位移量来表示
如果调用函数时出错,ftell函数返回值为-1L

3. 随机读写

九. 文件读写的出错检测

1. ferror函数

在调用各种输入输出函数(如putc、getc、fread、fwrite等)时,如果出现错误,除了函数返回值有所反映外,还可以用ferror函数检查

函数调用形式:ferror(fp);
返回值:为0表示未出错,返回非零表示出错

2. clearerr函数

函数作用:使文件出错标志和文件结束标志置为0

当调用一个输入输出函数时出现错误,ferror函数值为一个非零值,立刻调用clearrr函数,
使ferror(fp)的值变为0,以便进行下一次的检测

你可能感兴趣的:(C语言程序设计)