文件基本概念:
- 文件的定义:文件是指存储在外部存储介质(外存,如:U盘、光盘、硬盘等等)上数据的集合。操作系统是以文件为单位对数据进行管理的。C语言将每一个与主机相连的输入或输出设备都看做是一个(广义的)文件。用文件可以长期保存数据,实现数据共享。
- 文件的使用和管理:在操作系统中提供了一个文件资源管理器,用来进行文件的管理。在程序运行时由程序在外存上建立或打开一个文件,通过写操作将数据存入该文件。同样,由程序打开外存上的某个已有文件,并通过读操作将文件中的数据读入内存供程序使用。文件的管理还包括:新建、移动、拷贝、粘贴、删除。在我们处理文件时,文件的路径是这样表示的:
(path,全目录)+文件名,如:D:/home/Sunner/main.c或D:\home\Sunner\main.c这种表示方法在C程序里面也是可以接受的。 - 文件的存储形式:按照文本存储的编码形式分为两类:文本(ASCII)文件和二进制文件。文本(ASCII)文件是指文件的每一个字节放一个ASCII代码,代表一个字符。在C语言中的源文件就属于文本文件。在源文件当中如果有一个整型常数123在文本文件中就占有3个字节,分别存放这3个字符的ASCII码。如果是整型常数1234,那么它在文件中占有4个字节,分别存放这4个字符的ASCII码。存储形式如下:
3个字节,每个字节的存储的是这个字节对应的ASCII码,第一个字节是1的ASCII码,转换为10进制是49,依次是2,3的ASCII码值。二进制文件是指把内存中的数据按其在内存中的存储形式原样输出到磁盘文件上。C语言源文件经过编译形成的目标文件就是一个二进制文件。例如,在目标文件中表示整型数字123,在内存中占有4个字节,1234在内存中也占4个字节:
因为我们在C语言中定义整型数在内存中都占有4个字节。
文件缓冲区:
文件缓冲区定义:指系统在内存中为每一个正在使用的文件开辟一个区域。之所以要开辟这样一个区域是因为:由于系统在磁盘文件数据的存储速度与内存数据存储访问的速度不同,文件的数据量较大时,数据的读写不可能瞬间完成,为了提高数据的存储速度,C程序对文件的处理采用缓冲文件系统方式进行,要求程序与文件之间有一个缓冲区,程序与文件的数据交换通过缓冲区来进行。对文件的操作通过一系列库函数来实现。
文件缓冲区示意图:
文件缓冲区分类:
缓冲文件系统:系统自动为每一个文件分配一个内存缓冲区。
非缓冲文件系统:系统不自动开辟确定大小的缓冲区,而由程序为每个文件设定缓冲区。
不同的操作系统对文件的处理采用不同的方式,UNIX用缓冲文件系统处理文本文件,用非缓冲系统处理二进制文件。标准C只采用缓冲文件系统。
文件的打开与关闭
C程序中文件的一般操作过程:
- 定义文件指针变量
- 打开文件——建立程序与文件的联系
- 操作文件——对文件进行读、写操作
- 关闭文件——切断文件与程序的联系
C程序对文件的操作通过库函数实现,这些函数均定义在<stdio.h>
中。
文件指针:
结构体类型 FILE:每个被使用的文件都在内存中开辟一个区域用来存放文件的有关信息。这些信息保存在一个结构体类型变量中。对文件操作的相关信息的结构体类型由系统定义为“FILE”,包含在studio.h文件中。具体定义如下:
定义文件指针变量:FILE * 变量名;例如FILE *fp;
fp是一个指向FILE结构体类型的指针变量。可以使fp指向某一个具体文件的结构体变量,通过读取该结构体变量中的有关信息来实现对文件的访问和操作。通过fp指定要被访问的文件。
- 文件的打开:
目的:在对文件读写之前打开文件,建立程序与文件的联系。
函数:FILE * fopen(const char *filename,const char *mode);
返回值为打开的文件指针,若失败则返回NULL,filename是打开的文件名字字符串,包含路径。如果不含路径,表示打开当前目录下的文件,mode是文件打开后的使用方式字符串。
三要素:文件指针,文件名,使用方式。
如:
FILE *fp1,*fp2;
fp1=fopen("m1","r");
fp2=fopen("d:/madongshenme/mydata.dat","w");
FILE *fp3=fopen("C:\\CONFIG.SYS","rb");
//rb代表读二进制文件
文件使用方式:
文件读写的一般规则:
读文件:指定的文件必须存在否则出错;
写文件(指定的文件可以存在也可以不存在):
以“w”方式写:if 该文件已经存在 原文件将被删除然后重新建立;else 按指定的名字新建一个文件;
以“a”方式写:if 该文件已经存在 写入的数据附加在原数据后边;else 按指定的名字新建一个文件(与“w”相同);
文件同时读写:使用“r+”、“w+”、“a+”打开文件。
- 文件的关闭:
函数:int fclose(FILE *fp);
功能:使用文件之后关闭文件。使文件指针变量不再指向该文件。
返回值:文件关闭成功返回0,若失败返回非零值。
fp:要关闭文件的文件指针,在程序结束之前要求关闭所有使用的文件:将缓冲区的数据输出到外存后再释放文件指针变量,可避免数据丢失。释放与文件联系的内存空间,以后可以重复使用。
如:
FILE *fp;
......
fclose(fp);
文件的顺序读写
完成了文件的打开操作后,我们就可以开始进行对文件的读写操作了,文件的读写操作分为顺序读写和随机读写,顺序读写就是从文件的头开始,依次按指定的大小读写下去,而随机读写就是根据用户的需要,将文件中的位置指针移到某个指定位置的读写方式。
- 文件的顺序读写:
格式化读写函数:
函数:
int fscanf(FILE *fp,const char *format,...);
格式:int fscanf(文件指针,格式字符串,输入对象表列);
第一个形参是文件指针,它指定了要读的文件对象,第二个是格式字符串,描述了要读的文件里面字符串的格式,第三个是一个输入对象表列,常用的是一系列的变量的取地址的表列。
功能:将指定磁盘文件格式化赋给指定对象。
栗子:
fscanf(fp,"%d,%f",&i,&a);
//从fp所指向的文件中,取出数据赋给i和a
函数:
int fprintf(FILE *fp,const char *format,...);
格式:int fprintf(文件指针,格式字符串,输出对象表列);
功能:将指定对象格式化输出到指定磁盘文件上。
栗子:
fprintf(fp,"%d,%f",a,b);
//将a和b的值按指定格式写入fp所指向的文件
栗子:将两个变量的数值写test.txt:
#include<stdio.h>
int main(){
FILE *fp;//定义文件指针变量
int i=3;
float t=4.5;
fp=fopen("test.txt","w");//打开文件
fprintf(fp,"i=%d,t=%.2f",i,t);//i,t写入文件
fclose(fp);//关闭文件
return 0;
}
进一步:
#include<stdio.h>
#include<stdlib.h>
int main(){
FILE *fp;//定义文件指针变量
int i=3;
float t=4.5;
if((fp=fopen("test.txt","w"))==NULL){//容错处理
printf("file open failed");
exit(0);
}
fprintf(fp,"i=%d,t=%.2f",i,t);//i,t写入文件
fclose(fp);//关闭文件
return 0;
}
函数名:exit(),所在头文件:stdlib.h,功能:关闭所有文件,终止正在执行的进程。exit(0)表示正常退出,exit(x)(x不为0)都表示异常退出。
字符方式读写函数:
函数:
int fgetc(FILE *fp);
函数的形参就是需要读取文件的文件指针。
功能:从指定文件读出一个字符。
返回值:若读成功,函数返回该字符(的ASCII码),若读到文件尾结束符时,函数返回一个文件结束标志EOF(值为-1)。
栗子:
char ch=fgetc(fp);
函数:
int fputc(int ch,FILE *fp);
功能:把一个字符写到fp所指向的文件中。
返回值:若写入成功,函数返回该字符(的ASCII码),否则返回文件结束标志EOF(值为-1)。
栗子:
char ch='a';
fputc(ch,fp);
栗子:将一个磁盘中的信息复制到另外一个磁盘中:
#include<stdio.h>
int 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);
fclose(in);
fclose(out);
return 0;
}
检测文件位置指针函数:feof()
调用一般形式:feof(文件指针);
功能:检测是否读到文件尾,若到文件尾,函数值为真(非0),否则为假(0)。
字符串读写函数:
函数:
char fgets(char *str,int n,FILE *fp);
功能:从fp所指的文件中读取字符串并在字符串末尾添加‘\0’,然后存入str,最多读n-1个字符。
返回值:当读到回车换行符、文件末尾或读满n-1个字符时,函数返回该字符串的首地址。
栗子:
char s[100];
fgets(s,100,fp);
函数:
int fputs(const char *s,FILE *fp);
功能:将字符串写入文件中。
返回值:若出现写入错误,则返回EOF,否则返回一个非负数。
栗子:
char *str="abcd";
fputs(str,fp);
栗子: 已有文本文件data3.txt,内容为Hello everyone!,读取第一个单词,屏幕显示后添加到文件的下一行。
#include<stdio.h>
#include<stdlib.h>
int main(){
FILE *fp;
char str[10];
int n=6;
if((fp=fopen("data3.txt","r"))==NULL){
printf("cannot open infile\n");
exit(0);
}
fgets(str,n,fp);
printf("substring is:%s\n",str);
fclose(fp);
if((fp=fopen("data3.txt","a"))==NULL){
printf("cannot open infile\n");
exit(0);
}
fputs("\n",fp);
fputs(str,fp);
fclose(fp);
return 0;
}
数据块读写函数:
函数:
unsigned fread(void *buffer,unsigned size,unsigned count,FILE *fp);
功能:从fp所指的文件中读取数据块并存储到buffer指向的内存中。
返回值:返回实际读到的数据块个数。
buffer是待读入数据块的起始地址
size是每个数据块的大小(待读入的每个数据块的字节数)。
count是最多允许读取的数据块的个数(每个数据块size个字节)。
栗子:
int a[3]; fread(a,4,3,fp);
函数:
unsigned fwrite(const void *buffer,unsigned siz e,unsigned count,FILE *fp);
功能:将buffer指向的内存中的数据块写入fp所指的文件。
返回值:返回实际读到的数据块个数。
栗子:
double b[3]; ...... fwrite(b,8,3,fp);
数据块读写函数应用:
作用:fread()、fwrite()用于读写数据块(可用来读写数组、结构变量的值等)。多用于读写二进制文件。
栗子:假设学生数据已经存放在磁盘文件中,可用以下for语句和fread函数读入30个学生的数据。
struct student stu[30];
......//打开文件
for(i=0;i<30;i++)
fread(&stu[i],sizeof(struct student),1,fp);
可用以下for语句和fwrite函数将内存中30个学生的数据输出到磁盘上。
for(i=0;i<30;i++)
fread(&stu[i],sizeof(struct student),1,fp);