第十三章 文件
13.1 文件概述
13.3 文件的打开与关闭
13.4 文件的顺序读写
13.5 文件的定位与随机读写
13.1 文件概述一,文件的概念
1,文件,指存储在外部介质上的数据的集合操作系统是以文件为单位对数据进行管理的,如果想使用存在外部介质上的数据,必须先按文件名找到指定的文件,然后再从文件中读取数据 ; 如果要向外部介质上存储数据,也必须先建立一个文件,再向它输出数据,
文件除了包括磁盘上存储的内容外,从操作系统的角度看与主机相连的各种输入输出设备也是文件,例如,键盘是输入文件,显示器是输出文件,
2,文件的分类
C语言把文件看作是一个字节的序列,即文件是由一个一个字节的数据顺序组成的,根据数据的组织形式把文件分为两类,文本文件和二进制文件,
(1) 文本文件 (也称 ASCII文件 )
文件的每一个字节存放一个 ASCII码,代表一个字符,
其优点是便于对字符进行处理,缺点是占用存储空间较多,二进制形式与 ASCII码形式转换需要时间
(2) 二进制文件把内存中的数据按其在在内存中的存储形式原样输出到磁盘上存放,其优点是节省空间,输入输出速度快 ;
缺点是不能直接输出字符形式设内存有一整数 2460,内存中的存储形式,
即十进制数 2460所对应的二进制数形式 0000 1001 1001 1100
2460在文本文件中的存储形式(非压缩的 BCD码),
即每位十进制数占 8为二进制数前 4位为填充位,后 4为十进制数值所对应的二进制形式。 0011 0010 0011 0100 0011 0110 0011 0000
2460在二进制文件中的存储形式,0000 1001 1001 1100
二,磁盘文件系统
1,磁盘文件系统对文件的处理方法从磁盘文件输入数据时 (即 读文件 ),先将数据送到“输入缓冲区”,再从缓冲区将数据传送给程序数据区 (即传给程序中的变量 ) ; 向磁盘文件输出数据时 (即 写文件 ),先将程序中变量的值送到“输出缓冲区”,等缓冲区装满后再将数据一起传送给磁盘文件,使用缓冲区的目的是为了减少对磁盘的实际读写次数
2,磁盘文件系统的分类
(1) 缓冲文件系统,系统 自动的 在内存区为每一个正在使用的文件开辟一个缓冲区,缓冲区的大小由具体的 C版本确定,一般为 512字节,
(2) 非缓冲文件系统,系统 不是自动 开辟确定大小的缓冲区,
由程序为每个文件设定缓冲区及其大小 (即缓冲区由用户根据需要自己进行设置 ),
磁盘文件 输入缓冲区 程序数据区读文件写文件 输出缓冲区三,文件类型指针要使用一个文件需要有一定的信息,如文件当前的读写位置,与文件对应的内存缓冲区地址,文件的操作方式等,
这些信息都存放在,文件信息区,中,“文件信息区”是一个结构体变量,其结构体类型由系统定义,类型名为 FILE
定义形式,FILE * 指针变量名 ;
注意,
1,只有通过文件指针变量才能调用相应的文件
2,有 n个文件就要定义 n个文件指针变量,分别对应各个文件
3,FILE必须大写
13.3 文件的打开与关闭一,文件的打开
1,fopen函数格式,fopen ( 文件名,文件使用方式 ) ;
fopen函数有返回值,返回值是一个地址,即被打开文件的“文件信息区”的起始地址,使用时应将返回值赋给一个文件指针变量 需要打开的文件名使用文件的方式 (读或写 )
让哪个指针变量指向被打开的文件例 FILE *fp ;
fp = fopen (,file1”,“r” ) ;
2,文件使用方式(见课本 P333 表 13-1)
(1),r”(文本文件)与,rb”(二进制文件) 只读以此方式打开文件时,该文件必须已经存在,如不存在将出错,打开文件后只能从文件中读数据,当前读写位置设定于文件开头,
(2),w” (文本文件)与,wb” (二进制文件) 只写以此方式打开文件时,如有同名文件 (即文件已存在 ),则将原有文件删除并建立一个新文件 ; 如没有同名文件 (即文件不存在 ),则建立一 个新文件,打开文件后只能向文件中写数据,
当前读写位置设定于文件开头,
(3),a” (文本文件)与,ab” (二进制文件) 追加以此方式打开文件时,如有同名文件,则将当前读写位置设定于文件末尾,可以追加数据 ; 如无同名文件,则建立一个新文件
(4) 带,+”号的形式,表示即能读又读写
3,打开文件的方法打开文件时,因使用方式不对或其他原因,可能会使打开文件的操作失败,这时 fopen函数的返回值是 NULL,所以在打开文件时通常我们会做一个判断,看打开操作是否成功,
例 if ( ( fp =fopen(“file1”,“r”) ) = = NULL )
{ printf(“Can not open this file!\n”) ;
exit( 0 ) ;
}
说明,exit 函数的作用是关闭所有文件,结束程序,并返回操作系统,也可写成 exit ( ) ;
注意,使用 exit函数要包含头文件 < stdlib.h >
二,文件的关闭
1,fclose函数格式,fclose ( 文件指针变量 ) ;
例,fclose( fp ) ;
作用,将文件指针变量所指向的文件关闭,即使文件指针变量不再指向该文件,以后不能通过文件指针变量对该文件进行操作说明,fclose函数的返回值为整型,若关闭成功返回值为 0,
若失败返回值为非零数
2,关闭文件的重要性写文件时,在“输出缓冲区”装满后,才将数据一起写入文件,
当程序结束时,缓冲区可能还未满,如未关闭文件就结束程序,
则缓冲区的数据不能写入文件,会丢失数据,使用 fclose函数,
不论缓冲区是否已满,都会将缓冲区的数据写入文件,再关闭文件,
13.4 文件的顺序读写一,输入 /输出一个字符
1,fputc函数 (向文件输出一个字符 )
格式,fputc ( 字符常量或字符变量,文件指针变量 ) ;
作用,将一个字符写到文件指针所指向的文件中去函数返回值,成功时返回该字符,失败时返回 EOF
例,FILE *fp ; char ch ;
if ( ( fp =fopen(“file1”,“w”) ) = = NULL )
{ printf(“Can not open this file!\n”) ; exit( 0 ) ; }
/*按只写方式打开文件 file1.c,若打开失败则结束程序 */
fputc(?s?,fp ) ;
ch = getchar( ) ;
fputc( ch,fp ) ; 将字符‘ s?写入 fp指向的文件 file1中将字符变量 ch中存放的字符写入 fp指向的文件 file1中
2,fgetc函数形式,字符变量 = fgetc( 文件指针变量 ) ;
作用,从文件指针变量所指向的文件中读一个字符,
并将它赋给程序中的一个字符变量函数返回值,成功时返回该字符,失败时返回 EOF
例,FILE *fp ; char ch ;
if ( ( fp =fopen(“file1”,“r”) ) = = NULL )
{ printf(“Can not open this file!\n”) ; exit( 0 ) ; }
/*按只读方式打开文件 file1.c,若打开失败则结束程序 */
ch = fgetc ( fp ) ; /*从打开文件中读一个字符赋给变量 ch*/
printf(“%c”,ch ) ;
注意,EOF 实际是一个符号常量,其值为 –1
一般用 EOF作字符文件的文件结束符,当用 fgetc
函数时,如果遇到文件结束也将返回 EOF
二,输入 /输出一个字符串
1,fgets函数格式,fgets( str,n,fp ) ;
说明,str -- 字符数组名,n -- 整数值,fp -- 文件指针变量作用,从 fp所指向的文件中读 (n-1)个字符,将它们存放到
str数组中,并在其后自动加一个‘ \0?,如果读入 (n-1)
个字符前遇到换行符或文件结束符 EOF,则结束读入函数返回值,成功返回 str数组的首地址,失败返回 NULL
2,fputs函数格式,fputs( str,fp ) ;
说明,str -- 字符串常量 或 字符数组名 或 字符指针变量
fp -- 文件指针变量作用,将 str字符串写到 fp所指向的文件中,但‘ \0?不写入文件函数返回值,成功返回 0,出错返回 非 0 值例,从键盘输入一字符串 str1,然后保存到磁盘文件,test”中;
再将文件中内容读出到字符串 str2中,比较 str1和 str2是否一致。
#include <stdio.h>
#include <stdlib.h>
void main( )
{ FILE *fp; char str1[20],str2[20] ; int n;
if ( ( fp =fopen(“test.c”,“w”) ) = = NULL )
{ printf(“can not open this file!\n”) ; exit( 0 ) ; }
/*将文件 tset以写方式打开 */
gets(str1); n=strlen(str1); /*输入字符串给字符数组 str1*/
fputc( str1,fp) ; /*将字符串存入文件 test中 */
fclose(fp); /*将 test文件关闭 */
fp=fopen(,test.c”,“r” ) ; /*将文件 tset以只读方式打开 */
fgets(str2,n+1,fp ); /*从文件 test中读 n个字符存入数组 str2中*/
puts(str1); /*输出字符数组 str1*/
puts(str2); /*输出字符数组 str2与 str1对照看是否一样 */
fclose(fp); /*将文件 test关闭 */
}
三,按“数据块”的方式输入输出
1,格式,fread ( buffer,size,count,fp ) ;
fwrite ( buffer,size,count,fp ) ;
2,说明
(1) buffer,是一个地址对 fread而言 buffer是从文件中读取的数据要存放的存储区的首地址对 fwrite,buffer是向文件中写入数据时存储变量的地址
(2) size,要读写数据所占用的字节数,即数据长度
(3) count,要读写数据项的个数
(4) fp,文件指针变量,指明写入哪个文件注意,用 fread,fwrite 必须采用二进制方式打开文件例,从键盘输入 10个整数,把它们保存到文件,datafile.c”中,
再从文件中读取这些整数,并输出到显示器上
#include <stdio.h>
#include <stdlib.h>
void main ( )
{ FILE *fp ; int m,n,j ;
if ( ( fp =fopen(“datafile.c”,“wb+”) ) = = NULL )
{ printf(“Can not open this file!\n”) ; exit( 0 ) ; }
/*以既读又写的二进制方式打开文件 datafile*/
for ( j=0 ; j<10 ; j++ )
{ scanf(“%d”,&m ) ;
fwrite( &m,2,1,fp ) ; /*将数 m存入文件 datafile中 */
}
rewind ( fp ) ;/*将文件位置指针重新返回到文件开头 */
for ( j=0 ; j<10 ; j++ )
{ fread ( &n,2,1,fp ) ;/*将文件指针指向的数据存入变量n*/
printf(“%4d”,n ) ; }
fclose (fp) ; }
四,格式化输入输出函数
fprintf ( 文件指针变量,格式字符串,输出表列 ) ;
fscanf ( 文件指针变量,格式字符串,输入表列 ) ;
例,#include <stdio.h>
#include <stdlib.h>
void main ( )
{ FILE *fp ; int a,b,c,d ;
if ( ( fp =fopen(“exfile.c”,“wb+”) ) = = NULL )
{ printf(“Can not open this file!\n”) ; exit( 0 ) ; }
scanf(“%d%d”,&a,&b) ;
fprintf( fp,“%d,%d\n”,a,b ) ;/*将 a,b的值输出到文件 exfile.c*/
rewind(fp) ;
fscanf( fp,“%d,%d”,&c,&d ) ;/*从文件 exfile.c中读取两个整数到变量 c,d中 */
printf(“%d,%d\n”,c,d ) ;
fclose(fp);
}
13.5 文件定位与随机读写一,文件的读写位置指针文件中有一个读写位置指针,指向当前读或写的位置,前面讲的文件读写都是顺序读写,此时位置指针对当前所指向的数据项进行读或写操作后,位置指针会自动指向下一个数据项,
如能控制位置指针的指向,就能实现随机读写,
二,文件定位
1,fseek函数格式,fseek ( 文件指针变量,位移量,起始点 ) ;
位移量,是一个 long int 型数据,表示以“起始点”为基点向前或向后要移动的字节数,正数表示 向前 移动
(文件头?文件尾 ); 负数表示 向后 移动起始点,表示从什么位置为基准移动,用数字代表
0 -- 文件开始处
1 -- 文件位置指针的当前指向
2 -- 文件末尾处函数返回值,成功返回 0,出错返回 非 0 值
2,ftell函数格式,长整型变量 = ftell ( 文件指针变量 ) ;
作用,得到文件中位置指针的当前指向,这个位置是相对于文件开头的位移量函数返回值,成功返回当前位置,出错返回 -1L
例,long n ;
n = ftell (fp ) ;
if ( n= = -1L ) printf(“error!”) ;
3,rewind函数 (无返回值 )
格式,rewind(文件指针变量 ) ;
作用,将文件位置指针重新返回到文件开头例,从键盘输入学生的信息,并保存在外存储器上的文件
studata.c中,然后再从文件 studata.c中读取学生信息并将学生信息输出到显示器上
#include <stdio.h>
#include <stdlib.h>
#define SIZE 4 /*学生个数为 SIZE*/
struct stud /*定义一个结构体类型 */
{ char name[10]; /*包含三个成员分量 */
int num;
int score;
} st[SIZE]; /*定义一个长度为 SIZE,类型为结构体类型的数组 */
long len=sizeof(struct stud); /*获得结构体类型的长度 */
void save( ) /*向文件中写入学生信息 */
{ FILE *fp; int i ;
if ((fp=fopen(“studata.c”,“wb”))= =NULL)
{ printf(“can not open this file!\n”);
exit(0);
} /*按写方式打开二进制文件 studata.c*/
for( i=0; i<SIZE; i++)
if( fwrite(&st[i],len,1,fp)!=1 )/*如果写入函数返回值不为 1
printf(“file write error!\n”); 则说明写入文件出错 */
/*用 for循环,将数组中 4个学生的信息写入文件 studata.c中 */
fclose(fp); /*写入信息后,将文件关闭 */
}
void main( )
{ FILE *fp; int i;
struct stud x; /*定义变量 */
for (i=0; i<SIZE; i++)
scanf(“%s%d%d”,st[i].name,
&st[i].num,&st[i].score);
/*用 for循环,向数组 st中输入 4个学生的信息 */
save( ); /*调用函数 save将数组中学生信息转入文件中 */
fp=fopen(“studata.c”,“rb”);
/*将二进制文件 studata.c按只读方式打开 */
for(i=0; i<SIZE; i++)
{ fread(&x,len,1,fp);
/*从文件 studata.c中长度为 len的信息给变量 x*/
printf(“%-10s%4d%4d\n”,x.name,x.num,x.score);
/*将变量 x的各个成员值输出 */
} /*用 for循环,从文件 studata.c中读取信息到变量 x并输出 */
fseek(fp,-2*len,2); /*从文件尾将文件指针向前移动长度为两个数组元素的位置,即指向第三个学生的信息 */
fread(&x,len,1,fp); /*将此学生信息读到变量 x中 */
printf(“%-10s%4d%4d\n”,x.name,x.num,x.score);
/*输出变量 x的各个成员的值 */
fclose(fp); /*关闭文件 */
}