一.文件的操作流程
文件的操作包括对文件本身的基本操作和对文件中信息的处理。首先,只有通过文件指针,才能调用相应的文件;然后才能对文件中的信息进行操作,进而达到从文件中读数据或向文件中写数据的目的。
具体涉及的操作:建立文件、打开文件、从文件中读数据或向文件中写数据、关闭文件等。一般的操作步骤如下:
(1)建立/打开文件。
打开文件是进行文件的读或写操作之前的必要步骤。打开文件就是将指定文件与程序联系起来,为下面进行的文件读写工作做好准备。如果不打开文件就无法读写文件中的数据。当为进行写操作而打开一个文件时,如果这个文件存在,则打开它;如果这个文件不存在,则系统会新建这个文件,并打开它。当为进行读操作而打开一个文件时,如果这个文件存在,则系统打开它;如果这个文件不存在。则出错。数据文件可以借助常用的文本编辑程序建立,就如同建立源程序文件一样,当然,也可以是其他程序写操作生成的文件。
(2)从文件中读数据或者向文件中写数据。
从文件中读取数据,就是从指定文件中取出数据,存入程序在内存中的数据区,如变量或数组中。向文件中写数据,就是将程序中的数据存储到指定的文件中,及文件名所指定的存储区中。
(3)关闭文件
关闭文件就是取消程序与指定的数据文件之间的联系,表示文件操作的结束。
在C语言中,所有的文件操作都是由文件处理函数来完成的。
二.文件和内存的交互处理
文件是存储在外存的,而计算机在处理数据时,CPU都是和内存进行交互的。在CPU处理数据之前,外存上的文件是如何放置到内存中的?
按照操作系统对磁盘文件的读写方式,文件可以分为缓冲文件系统和非缓冲文件系统。
C语言的文件处理函数按照有无提供缓冲区分为提供缓冲区的标准输入、输出(Standard I/O)函数和不提供缓冲区的系统输入、输出(System I/O)函数。
所谓缓冲区,是指数据在访问时,为了加快程序运行的速度,在内存中事先建立一块区域来存放部分数据,接着再通过这个区域来访问整块数据,而不直接对磁盘进行访问。即系统自动地在内存区为每一个正在使用的文件名开辟一个缓冲区,他是内存中的一块区域,用于进行文件读写操作时数据的暂存,一般为512字节,这与磁盘的读写单位一致。
从内存中向磁盘输出数据必须先送到内存中的缓冲区,装满缓冲区后才一起送到程序数据区(程序变量)。在进行文件的读操作时,将磁盘文件中的一块数据一次读到文件缓冲区中,然后从缓冲区中取出程序所需的数据,送入程序数据区中指定变量或数组元素所对应的内存单元中。
采用文件缓冲系统的原因:
(1)磁盘文件的存取单位是“块”,一般一块为512字节。也就是说,从文件中读取数据或向文件中写数据,就要一次读或写512字节。而在程序中,给变量或数组元素赋值却是一个一个进行的。
(2)与内存相比,磁盘的读写速度是很慢的,如果每读或写一个数据就要和磁盘打一次交道,那么即使CPU的速率很高,整个程序的执行效率也会大打折扣。显然,文件缓冲区可以减少与磁盘打交道的次数,从而提高程序的执行效率。
1.单字符读写函数
字符读写函数是以字符(字节)为单位的读写函数。每次可从文件读出或向文件写入一个字符。
1)单字符读函数fgetc( )
函数原型:
int fgetc(FILE *fp);
其功能是:读取文件指针 fp目前所指文件位置中的字符,读取完毕,文件指针自动往下移一个字符位置,若文件指针已经到文件结尾,返回-1。
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#define LEN 100
int main()
{
FILE *fp;
char filename[LEN];
int fno,fsize,p;
char ch;
printf("请输入要打开文件的完整路径及文件名:\n");
gets(filename);
fp=fopen(filename,"rt");
if(fp==NULL)
{
printf("\n不能打开,%s 可能不存在\n",filename);
exit(1);
}
fno=fileno(fp);
fsize=filelength(fno);
printf("\n%s 文件打开!\n",filename);
printf("\n 文件大小 %d Byres\n",fsize);
printf("\n 文件内容为:");
while((ch=fgetc(fp))!=EOF)
printf("%c",ch);
fclose(fp);
}
2)写单字符函数fputc( )
函数原型:
int putc(char ch, FILE *fp);
其功能是:把字符 ch写入文件指针 fp所指向文件的位置,成功时返回字符的ASCII码,失败时返回EOF。
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <string.h>
#include <ctype.h>
#define LEN 100
int main()
{
FILE *fp;
char filename[LEN],data[LEN];
int fno,fsize,i;
char ch;
printf("请输入要打开文件的完整路径及文件名:\n");
gets(filename);
fp=fopen(filename,"a+"); //文件以追加方式写;
if(fp==NULL)
{
printf("\n不能打开,%s 可能不存在\n",filename);
exit(1);
}
fno=fileno(fp);
fsize=filelength(fno);
printf("\n%s 文件打开!\n",filename);
printf("\n 文件大小 %d Byres\n",fsize);
printf("\n 文件内容为:");
while((ch=fgetc(fp))!=EOF)
printf("%c",ch);
while(1)
{
printf("\n\n请问是否要添加数据(Y/N):");
if(toupper(getche())=='Y') //toupper()函数为大小写转换函数;
{
printf("\n\n请输入要添加的数据:");
gets(data);
for(i=0;i<strlen(data);i++)
fputc(data[i],fp);
}
else //不添加新内容;
{
fclose(fp);
break;
}
}
fp=fopen(filename,"rt");
if(fp==NULL)
{
printf("\n\n打开文件失败,%s 可能不存在\n",filename);
exit(1);
}
fno=fileno(fp);
fsize=filelength(fno);
printf("\n%s 文件打开!\n",filename);
printf("\n 文件大小 %d Byres\n",fsize);
printf("\n 文件内容为:");
while((ch=fgetc(fp))!=EOF)
printf("%c",ch);
fclose(fp);
printf("\n\n");
}
2.字符串读写函数
1)读字符串函数fgets( )
函数原型:
char *fgets(char *str, int n, FILE *fp);
其功能是:在文件指针fp所指文件位置读取n个字符并放入str字符数组中。如果读不到字符串,则返回NULL。
FILE *fp;
char str[10];
fp=fopen("e:\\code\\test.txt","rt");
while((fgets(str,10,fp))!=NULL)
printf("%s",str);
其意义是从 fp所指文件中读取10个字符送入字符数组str中,接着再将str字符串打印出,若文件读不到数据则返回NULL,此时会离开循环。
2)写字符串函数fputs( )
函数原型:
int fputs(char *str, FILE *fp);
其功能是:将字符串str写入文件指针fp所指文件的位置。写入数据成功时返回非0值, 写入失败时返回EOF。其中,字符串str可以是字符串常量,也可以是字符数组名或指针变量。
FILE *fp;
char str[10];
fp=fopen("e:\\code\\test.txt","rt");
gets(str);
fputs(str,fp);
fgets函数和fputs函数实例:
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <string.h>
#include <ctype.h>
#define LEN 100
int main()
{
FILE *fp;
char filename[LEN],data[LEN],temp[LEN];
int fno,fsize,i;
char ch;
printf("写文件程序....\n");
printf("请输入要打开文件的完整路径及文件名:\n");
gets(filename);
fp=fopen(filename,"a+"); //文件以追加方式写;
if(fp==NULL)
{
printf("\n不能打开,%s 可能不存在\n",filename);
exit(1);
}
fno=fileno(fp);
fsize=filelength(fno);
printf("\n%s 文件打开!\n",filename);
printf("\n 文件大小 %d Bytes\n",fsize);
printf("\n 文件内容为:");
while((fgets(temp,LEN,fp))!=NULL)
printf("%s",temp);
while(1)
{
printf("\n\n请问是否要添加数据(Y/N):");
if(toupper(getche())=='Y') //toupper()函数为大小写转换函数;
{
printf("\n\n请输入要添加的数据:");
gets(data);
fputs(data,fp);
}
else //不添加新内容;
{
fclose(fp);
break;
}
}
fp=fopen(filename,"rt");
if(fp==NULL)
{
printf("\n\n打开文件失败,%s 可能不存在\n",filename);
exit(1);
}
fno=fileno(fp);
fsize=filelength(fno);
printf("\n%s 文件打开!\n",filename);
printf("\n 文件大小 %d Byres\n",fsize);
printf("\n 文件内容为:");
while((fgets(temp,LEN,fp))!=NULL)
printf("%s",temp);
fclose(fp);
printf("\n\n");
}
3.格式化字符串读写函数
1)格式化字符串读函数 fscanf( )
函数原型:
int fscanf ( FILE *fp, "格式化字符串", 输入项地址表 );
其功能是:从文件指针 fp所指向的文件中按照格式字符串指定的格式,将文件中的数据送到输入项地址表中。若读取数据成功会返回所读取数据的个数,并将数据按照指定格式存入内存中的变量或数组中,文件指针自动向下移动;若读取失败则返回EOF。
fscanf(fp,"%s□%s□%s",num,name,sex); //□可代表空格;
输入数据可显示,04211099□王浩阳□男;
2)格式化字符串写函数fprintf( )
函数原型:
int fprintf(FILE *FP, "格式化字符串", 输出项地址表);
其功能是:将输出项表中的变量值按照格式字符串指定的格式输出到文件指针fp所指向的文件位置。
提前初始化数据:char num[20]="04211100",name[40]="张书源",sex[5]="女";
fprintf(fp,"%s□%s□%s",num,name,sex); //□可代表空格;
写入文件数据为,04211110□张书源□女;
fscanf和fprintf的应用:
/*从键盘上输入一个班 N个学生的数据,并保存到磁盘文件中,再读出该班学生的数据显示在屏幕上*/
/*从键盘上输入一个班 N个学生的数据,并保存到磁盘文件中,再读出该班学生的数据显示在屏幕上*/
#include <stdio.h>
#include <stdlib.h>
#define N 3
struct stu
{
char num[20];
char name[40];
char sex[5];
}class[N];
void main()
{
FILE *fp;
int i;
printf("\n输入该班级的数据:\n");
fp=fopen("class.txt","wt");
if(fp==NULL)
{
printf("不能打开此文件,按任意键退出!");
getch();
exit(1);
}
for(i=0;i<N;i++)
{
printf("\n第%d个人的信息:\n",i+1);
printf("\n学号:");
gets(class[i].num);
printf("\n姓名:");
gets(class[i].name);
printf("\n性别:");
gets(class[i].sex);
fprintf(fp,"%s @%s $%s\n",class[i].num,class[i].name,class[i].sex);
}
fclose(fp);
fp=fopen("class.txt","rt");
printf("该班级数据为: \n");
printf("学号 姓名 性别\n");
i=0;
while(fscanf(fp,"%s %s %s",class[i].num,class[i].name,class[i].sex)!=EOF)
{
printf("%s %s %s\n",class[i].num,class[i].name,class[i].sex);
i++;
}
fclose(fp);
}
4.数据块读写操作
1)数据块读函数 fread( )
函数原型:
int fread (void *buffer, int size, int count, FILE *fp);
其功能是:从文件指针 fp 所指向的文件的当前位置开始,一次读入 size 个字节,重复 count 次,并将读取到的数据存到 buffer 开始的内存区中,同时将读写位置指针后移 size*count次。该函数的返回值是实际读取的count值。
函数 fread( )内各个参数的含义如下:
- buffer 是一个指针,在 fread 函数中,它表示存放读入数据的首地址(即存放在何处)。在 fwrite 函数中,他表示要输出的数据在内存中的首地址(即从何处开始存储)。
- size 表示数据块的字节数。
- count 表示要读写的数据块块数。
- fp 表示文件指针。
//例如
float fa[5];
fread(fa,4,5,fp);
//其意义是从 fp 所指的文件中,每次读 4 个字节(一个实数)送入实型数字 fa 中,
//连续读 5 次,即读5个实数到 fa 中。
2)数据块写函数 fwrite( )
函数原型:
Int fwrite(void *buffer, int size, int count, FILE *fp);
其功能是:从buffer 所指向的内存区开始,一次输出 size 个字节,重复 count 次,并将输出的数据存入到 fp 所指向的文件中,同时将读写位置指针后移 size*count 次。
//举例
float fa[5];
fwrite(fa,4,5,fp);
//其意义是从 fa 实型数组中,每次读 4 个字节(一个实数)写入 fp 所指的文件中,
//连续读写 5 次,即将 5 个实数写到 fp 所指的的文件中。
fread和fwrite的应用:
/*从键盘上输入一个班 N个学生的数据,并保存到磁盘文件中,再读出该班学生的数据显示在屏幕上*/
/*从键盘上输入一个班 N个学生的数据,并保存到磁盘文件中,再读出该班学生的数据显示在屏幕上*/
#include <stdio.h>
#include <stdlib.h>
#define N 5
struct stu
{
char num[20];
char name[40];
char sex[5];
}class[N];
void main()
{
FILE *fp;
int i;
printf("\n输入该班的数据:\n");
fp=fopen("class1.txt","wt");
if(fp==NULL)
{
printf("不能打开此文件,按任意键退出!");
getch();
exit(1);
}
for(i=0;i<N;i++)
{
printf("\n第%d个人的信息:\n",i+1);
printf("\n学号:");
gets(class[i].num);
printf("\n姓名:");
gets(class[i].name);
printf("\n性别:");
gets(class[i].sex);
fwrite(&class[i],sizeof(struct stu),1,fp);
}
fclose(fp);
fp=fopen("class1.txt","rt");
printf("该班级数据为:\n");
printf("学号 姓名 性别\n");
i=0;
while(fread(&class[i],sizeof(struct stu),1,fp)!=NULL)
{
printf("%s %s %s\n",class[i].num,class[i].name,class[i].sex);
i++;
}
fclose(fp);
}
5.文件的随机读写
前面介绍的文件读写方式都是顺序读写,即读写文件只能从头开始,顺序读写各个数据。但在实际问题中常要求只读写文件中某一指定的部分。为了解决这个问题,可移动文件内部的位置指针到需要读写的位置,再进行读写,这种读写称为随机读写。实现文件随机读写的关键是按要求移动位置指针,这称为文件的定位。
1)函数rewind( )
函数原型:
void rewind(FILE *fp);
其功能是:将文件内部的位置指针移到文件的开始位置。