Transcript Document

第十章
文件
10.1 文件概述
■文件概念
所谓文件就是:存储在外部介质上
的信息集合。
根据存储的介质不同可分为:
磁盘文件、磁带文件等。
根据内容的不同可分为:
程序文件、数据文件等。
■使用文件输入输出的必要性
这里主要讨论数据文件的输入输出,即
如何将文件中的数据“输入”到程序的数据
结构中,如何将程序的数据结构中的数据
“输出”到文件中。
以往的输入输出方法:
键盘输入 ,屏幕输出。
这种方法不适用于数据量大的情况。
举例说明:
main()
{ int i, a[1000];
for(i=0;i<1000;i++)
scanf(“%d”,a+i);
┊
for(i=0;i<1000;i++)
printf(“%5d”,a[i]);
}
缺 点:
①手工输入,
效率低下,容
易出错。
②输出的数据
不能保存,不
便于进一步使
用。
}
采用文件输入输出可以克服这些缺点。
main()
{
int i, a[1000];
for(i=0;i<1000;i++)
scanf(“%d”,a+i);
┊
for(i=0;i<1000;i++)
printf(“%5d”,a[i]);
}
文件
文件
■ C文件分类
按在磁盘上存储的形式不同,可分为:
文本文件:
以ASCII字符存放—可见、可编辑、占
空间大,读写效率低。
二进制文件:
以二进制形式存放—可读性差、占空间
小,读写效率高。
使用时可根据需要选择。
10.2 文件类型指针
每个被使用的文件都在内存中开辟一
个区,用来存放文件的有关信息(如文件
名、文件状态及文件当前位置等)。这些
信息保存在一个FILE类型的结构体变量中
。
若 FILE *fp; 则fp就称为指向文件类
型的指针变量。访问文件通过文件指针进
行。
FILE结构体类型是由系统定义的。
具体定义如下:
typedef struct
{ short
level;
unsigned
flags;
char
fd;
unsigned
char hold;
short
bsize;
unsigned char *buffer;
unsigned char *curp;
unsigned
istemp;
short
token;
}FILE;
缓冲区“满”或“空”的程度
文件状态标志
文件描述符
如无缓冲区不读取字符
缓冲区的大小
缓冲区的位置
当前读写指针
临时文件,指示器
用于有效性检验
10.3 文件的打开与关闭
对文件的读写之前应“打开”该文件。
使用结束后“关闭”此文件。
■文件的打开(fopen 函数)
用fopen函数实现对文件的打开。
fopen函数调用的一般形式:
FILE *fp;
fp=fopen(文件名,读写方式);
例如:
fp=fopen(“a1.txt”, ”r”);
以只读方式打开文件a1.txt。
fopen函数返回指向a1.txt文件的指
针,即fp是指向a1.txt文件的指针变
量,往后就可以通过fp访问a1.txt文
件。
文件读写方式:
“r”
“w”
“a”
“rb”
“wb”
“ab”
按只读方式打开一个文本文件
按只写方式打开一个文本文件
按追加方式打开一个文本文件
按只读方式打开一个二进制文件
按只写方式打开一个二进制文件
按追加方式打开一个二进制文件
说明:
(1)不能用”r”方式打开一个不存在的文
件,”r”方式只读不能写。
(2)“w” 方式只写不能读,具有建立和
覆盖功能。
(3)调用fopen函数时,如果返回NULL则
表示打开不成功。
■文件的关闭(fclose 函数)
在使用完一个文件后应用fclose 函数关
闭文件,形式为:
fclose(文件指针);
如:
fclose(fp);
关闭后fp不再指向该文件。
10.4 文件的读写
文件打开后,就可以对它进行读写了。
■文本文件的读写
即如何将以文本方式存放的文件输入到程
序的数据结构中。如何将程序的数据结构中
的数据以文本方式输出到文件中。
读对象:以文本方式存放的文件
写对象:以文本方式存放的文件
用于对文本文件读写的函数有:
fscanf
fprintf
fgetc, getc
fputc, putc
fgets
fputs
重点介绍 fscanf 和 fprintf。
以例子说明fscanf和fprintf的使用。
例 : 已 知 文 本 文 件 f1.txt 中 存 放 有
100个学生的分数,要求读入这些数据,
并按从高到低的顺序排序后输出到另一
文件中。
#include “stdio.h”
void sort(int *a,int n)
{ ……}
main()
{
int i,a[100];
FILE *fp;
fp=fopen(“f1.txt”, “r”);
if(fp==NULL) exit(0);
定义一个指
向文件的指
针变量
打开文件,
使fp指向文
件f1.txt
for(i=0;i<100;i++)
fscanf(fp,”%d”,a+i);
从fp所指
的文件中
读数据
fclose(fp);
关闭fp所
指的文件
sort(a,100);
fp=fopen(“f2.txt”, “w”);
for(i=0;i<100;i++) fprintf(fp,”%4d”,a[i]);
fclose(fp);
}
注意:文本文件的输入格式要与
文件中的数据格式匹配。
■二进制文件的读写 (数据块)
即如何将以二进制方式存放的文件输入
到程序的数据结构中。
如何将数据结构中的数据以二进制方式
输出到文件中。
读对象:以二进制方式存放的文件
写对象:以二进制方式存放的文件
读写函数:
fread
getw
fwrite
putw
例:将前例中的排序结果改用二进制方式
输出到文件f3.dat中。
#include “stdio.h”
struct student {
int num; char name[20];
};
void sort (int *a,int n)
{ ……}
main()
{
int i; struct student a[100];
FILE *fp;
fp= f open(“f1.txt”, “r”);
if(fp==NULL) exit(0);
int score;
for(i=0;i<100;i++)
fscanf(fp,”%d %s %d”,&a[i].num,&a[i].name,a[i].score);
fclose1(fp);
sort(a,100);
文件的指
针
fp=fopen(“f3.dat”, “wb”);
for(i=0;i<100;i++)
fwrite(a+i, sizeof(struct student), 1, fp );
fclose(fp);
数据的开
始地址
数据的每一
项的长度
数据的
项数
//将“f3.dat”数据读入内存并输出到屏幕
fp=fopen(“f3.dat”, “rb”);
for(i=0;i<100;i++) {
fread(a+i,sizeof(struct student),1,fp);
printf(“num:%-5d name:%-10s score:% 3d”, a[i].num,
a[i].name, a[i].score);
}
fclose(fp);
}
10.5 文件的定位
文件中有一个位置指针,指向当前读写
位置。如果顺序读写一个文件,每次读写完
一个字符后,该位置指针自动指向下一个字
符位置。如果想改变这样的规律,强制使位
置指针指向指定位置,可以用有关函数。
■rewind函数
rewind函数的作用是使位置指针重返
回文件的开头。
例:对文本文件f1.txt中的100个分数
求超过平均分的人数。
#include “stdio.h”
main()
{ int i,a,n=0;
float aver=0;
FILE *fp;
fp=fopen(“f1.txt”,“r”);
for(i=0;i<100;i++)
{
fscanf(fp,”%d”,&a);
aver+=a;
}
aver/=100;
rewind(fp);
for(i=0;i<100;i++)
{
fscanf(fp,”%d”,&score);
if(score>aver) n++;
}
fclose(fp);
printf(“\n %d”,n);
}
■fseek函数和随机读写
使用fseek函数可以将位置指针指向所需
的位置。
fseek函数调用的一般形式:
fseek(文件指针,位移量,参考点);
以起始点为基
准,向前移动
的字节数
0 或 SEEK_SET
1 或 SEEK_CUR
2 或 SEEK_END
文件开始
当前位置
文件末尾
例:如果fp是指向一个存放100个整数的二进
制文件,要读取第50个数到变量n时:
fseek(fp,sizeof(int)*(50-1),SEEK_SET);
fread(&n, sizeof(int), 1,fp);
例:如果fp是指向一个存放100个整数的文
本文件,并已知每个数按3位数字的定长格式
存放,要读取第50个数到变量n时:
fseek ( fp, 3*(50-1), SEEK_SET );
fscanf ( fp, ”%3d”, &n);
若要从当前位置跳过10个数后读取一个数:
fseek ( fp, 3*10, SEEK_CUR );
fscanf ( fp, ”%3d”, &n);
例:已知文本文件f5.dat中存放有100个学
生的学号、姓名和考试成绩;要求从键盘输入
任一学号,检索出相应学生的数据。
说明:
(1)文件f5.dat中每行为一个学生的数据,按定
长格式存放,依次为:学号(整数,占5格)、姓名
(占10格)、成绩(整数,占4格)。
(2)按学号从小到大的顺序连号存放,起始学号
为1001。
#include “stdio.h”
typedef struct
{
int num;
char name[20];
int score;
}STU;
main()
{
int no;
STU st;
FILE *fp;
fp=fopen ( “f5.dat” , ”r” );
scanf ( “%d” , &no );
fseek ( fp, (no-1001)*19, 0 );
1001 LiLi
90
1002 WangPing 100
1003 HuHeng 75
┊
fscanf ( fp, ”%5d%10s%4d” ,&st.num
,st.name, &st.score );
printf (“\n%5d%10s%4d” , st.num ,
st.name, st.score ) ;
fclose ( fp ) ;
}
上例的检索方法称为“定位检索”。
如果是非定长格式,则需要用“遍历检索”
。
while ( !feof ( fp ) )
{ fscanf ( fp, ”%d%s%d”, &st.num,
st.name, &st.score );
if ( st.num==no )
{ printf ( “\n%5d%10s%4d”, st.num,
st.name, st.score );
break;
}
}
速度慢,但不受限制
综合例:
已知文本文件f1.dat中存放有武汉市所有
公民的有关性别和年龄的数据,请编写程序分
别找出其中10名男寿星和10名女寿星,并将20
名寿星的数据以文本文件的方式存入到文件
f2.dat中(先男后女)。
说明:
① 文件f1.dat中每行为一个公民的数据,共有3项
,依次为:姓名(不超过10个字符)、性别(0表示
男,1表示女)和年龄(整数),项间以空格分隔。
② 未给出公民个数,将文件中的数据读完为止。
算法思想:
开辟一个存放20名寿星数据的结果表
a(结构体数组),然后逐个读取公民
数据,每读取一个就向a中“判断插入
”一个,男性公民往前段插,女性公民
往后段插。
寿星表a
读一个公民
的数据到p
男性插入
女性插入
N
读 完 否
?
Y
Wanghao 0 100
Liming 0 98
┊
wudan
1
99
xiaofang 1
95
┊
#include <stdio.h>
typedef struct
{ char name[10];
int sex;
int age;
} PEP;
插入函数,将一个公民的数据插入到寿星表
void insert (PEP *a, int n, PEP t )
{ int i,j;
if ( t.age < a[n-1].age ) return ;
for ( i =0; i<n; i++) if (t.age>a[i].age) break;
for ( j=n-1; j>i; j--) a[j]=a[j-1]; 移位
a[i]=t;
插入
}
main()
{ int j; PEP p,a[20];
FILE *fp;
fp=fopen (“f1.dat”,”r”);
if(!fp) exit(0);
for (j=0;j<20;j++) a[j].age=0;
while( !feof ( fp ) )
{ fscanf ( fp, ”%s%d%d”, p.name,
&p.sex, &p.age );
insert (a+10*p.sex,10,p);
}
fclose(fp);
fp=fopen(“f2.dat”,”w”);
for(j=0;j<20;j++)
fprintf(fp,”\n%15s%2d%5d”,
a[j].name, a[j].sex,a[j].age );
fclose ( fp );
}