一 文件
1 C文件概述
文件( file ) 是指存储在外部介质上数据的集合。操作系统则是以文件为单位对数据进行管理的.
C 语言中文件是流式文件,即文件是一个字节流或二进制流.并以字节为单位进行存取.
根据文件中数据的存储方式,C 语言的文件可分为:
文本文件(ASCII码文件)和二进制文件。
1. 文本文件
文件中每一个字符占一个字节,按其ASCII码存放。
特点: 可直接阅读,也可按字符逐个处理。但占用存储空间较大,同时需要额外的转换时间.
转换
数据写入文件时:二进制码转换成ASCII码
数据读入内存时:ASCII码转换成二进制码
2. 二进制文件
二进制文件是将数据按其在内存中的存放形式直接存入文件中.
特点:节省存储空间和转换时间,但不能直接阅读。一般用于存放数据或中间结果,以提高处理速度。
C 程序中对文件的处理是通过调用系统提供的输入输出函数来实现的。
它把所有的外部设备都作为文件对待,这样的文件也叫设备文件。在 C 语言中把一般磁盘文件和设备文件都作为相同的逻辑文件对待,对它们的输入输出采用相同的方法进行。
C 语言中可采用“缓冲文件系统”和“非缓冲文件系统”来处理文件.
缓冲文件系统:
系统自动在内存区为每一个正在使用的文件开辟
一个缓冲区,数据的输入和输出都先经过缓冲区。
新标准C中规定,只采用“缓冲文件系统”。
2.文件类型指针
对文件的处理是通过系统自动定义的文件结构体类型来进行的。它在“stdio.h”文件中。如:
typedef struct
{
int fd; /* 文件号*/
int cleft; /* 缓冲区中剩下的字节*/
int mode; /* 文件操作模式*/
char *nextc; /*下一个字节位置*/
char *buff; /* 文件缓冲区位置*/
} FILE ;
文件结构体一般习惯取名为 FILE 。
文件打开时,系统自动建立该文件的文件结构体,关闭文件后,该文件的结构体就被释放。
C语言对文件的操作是通过文件指针来实现的。而文件指针是通过文件结构体类型来定义的。如:
FILE *fp ;
fp 是一个指向 FILE 结构体类型的指针变量(简称文件指针 )。通过 fp 就能够打开和关闭与之相关的文件。
另外,C 语言中有三个标准文件指针由系统分配和控制,用户不能控制其开闭。
标准输入文件: 分配给键盘. 文件指针: stdin
标准输出文件: 分配给显示器. 文件指针: stdout
标准错误输出文件: 分配给显示器. 文件指针: stderr
程序开始运行时,系统自动打开三个标准设备文件.
二 函数
1.fopen ( )函数
用 fopen ( ) 函数来打开文件.
格式: FILE *fp ;
fp = fopen(文件名, 使用文件方式);
文件名:(1)可以是用双引号括起来的文件名字。
(2)也可以是存放文件名字符串的首地址。
fopen 函数的返回值是所要打开文件的指针。
如: FILE *fp ;
fp=fopen(“f1.c”,“r”);
被打开的文件名前面可以指明盘符和路径。如:
fp=fopen(“d:\\user\\stu.dat”,“r”);
说明:
1. 使用方式: 常见的有 “r”,“w”,“rb”,“wb”,“ r+”,“w+” 等。
2. 文件使用方式说明:
(1) “r”方式: 用于打开一个已存在的文本文件 。
只读,打开时,文件位置指针自动指向文件
开头。
(2) “w”方式: 该方式打开的文本文件。
只写,如不存在该文件,则新建一个指定的
文件名的文件;如该文件已存在,则打开时
先删除该文件,然后重建立一个新文件.
(3) “a” 方式: 该方式用于向文本文件
末尾添加数据。
文件打开时,位置指针自动指向文件末尾。
如文件不存在,则创建一个新文件。
(4) “r+”,“w+”,“a+” 方式:
这三种方式打开的文件均可进行读写操作。
r+: 先读后写被打开的文件应存在,位置指针指向文件开头。如原文件中有内容时,则写入的内容会覆盖原文件内容。
w+:先写后读。用该方式打开的文件,其内容全部丢失,指针指向文件开头.
a+: 用该方式打开的文件内容保留。位置指针自动指向文件末尾,可以追加数据,然后再读入数据。
另外还有: rb , wb , ab , rb+ , wb+ , ab+ 等方式,其含义同前述,只是用于二进制文件而已.
注意:不同的 C 系统,在文件打开方式的的表示上可能不相同,具体随所用的系统而定。
3. 正常打开文件时,fopen 函数返回指向该函数的指针。如不能打开文件,返回一个空指针值NULL。NULL在stdio.h 文件中已定义为0。
*常用固定格式:
FILE *fp;
if((fp=fopen(“f1.c”,“r”))==NULL)
{
printf(“Cannot open this file\n”);
exit(0);
}
其中exit函数的作用是关闭文件,终止正在调用的过程。exit(0)是正常退出程序,exit(!0)是表示某种类型错误。
2.fclose()关闭文件
用 fclose 函数来关闭文件.
格式: fclose ( 文件指针 ) ;
如: fclose(fp);
fclose 函数也带回一个返回值,正常关闭文件时返回值为 0, 反之返回值为非0。 可以用ferror函数来测试.
3.fputc()函数
格式: fputc(ch,fp)
作用: 将一个字符(ch)输出到 fp所指向的文件中去。
返回值:成功:则返回值就是所输出的字符;
失败:则返回EOF符号常量. stdio.h 中 EOF
被定义为-1。
前面所讲的 putchar 函数实质上是由 fputc 函数派生出来的。
#define putchar(c) fputc(c, stdout)
4. fgetc 函数
格式: ch=fgetc(fp)
作用: 从指定的文件中读入一个字符(对文本文件)或一个字节的数据(对二进制文件),该文件必须是以读或读写方式打开的。
返回值:正常请况下,fgetc 函数返回一个字符并赋给 ch。如遇文件结束符,则返回一个文件结束标志 EOF。
说明:
文本文件: 由于字符的ASCII码不可能为-1,故
EOF定义为 -1是合适的。
二进制文件:数据的值有可能是-1,以EOF作为文件结束则不合适。因此,常用 feof 函数来测试文件是否真正结束。
feof函数使用
使用格式: feof(fp)
返回值: 文件结束,则返回值为1,反之为 0。
这种判别方法对二进制文件和文本文件均适用。
如前例中的 while 语句可改为:
while(!feof(fp))
5.fwrite和fread函数
1.1fwrite函数:
把结构体数据写入磁盘使用fwrite函数;
格式为:fwrite(buffer,size,count,fp);
其中buffer为待写入磁盘数据的结构体首地址;
size:一次写入的字节数,即sizeof(struct student);
count:为写入count次size大小的数据;
fp:文件指针,指向待写入磁盘文件的地址;
代码段:
struct student
{
char name[20];
int num;
int year;
int score;
}stu[2];
.....
if(fwrite(&stu[i],sizeof(struct student),1,out)!=1)
{
printf("Error write\n");
}
printf("sucess write %d\n",i+1);
....
测试分析:
写入几个结构体数据后,打开保存进磁盘文件的数据和待写入的数据不一致;
1:学生的数据是ASCII码,送入内存时,由内存以“wb”方式输出到磁盘文件时不发生字符阻转化,即按内存中存储形式原样输出到磁盘文件上;
2:fwire和fread函数一般用于二进制文件的输入和输出。因为它们是按数据块的长度处理输入输出的,在字符发生转换时,可能出现与原设想的不同的结构;比如用scanf函数输入的话,空格也会存入stu[i]中,显然不符;
明天使用fread函数读出已写入磁盘文件的数据,看看是什么妖魔鬼怪。
1.2fread函数
格式为:fread(buffer,size,count,fp);
其中buffer为待读入磁盘数据的结构体首地址;
size:一次写入的字节数,即sizeof(struct student);
count:为写入count次size大小的数据;
fp:文件指针,指向待写入磁盘文件的地址;
代码段:
for(i=0;i<2;i++)
{
// while (!feof(fp))//feof返回0 说面未结束
// {
//ch = fgetc(fp);
//putchar(ch);其中未必全是char 型数据,这是结构体,所以不能使用fgetc+puchar
// }
fread(&stu[i],sizeof(struct student),1,fp);//使用fp指向要读的文件,&stu[i] 是读出的首地址
printf("%s %d %d %d",stu[i].name,stu[i].num,stu[i].year,stu[i].score);
printf("\n");
}
对于读结构体数据使用fread函数,而不能使用fgetc+puchar;
测试分析:
6:fprintf 和 fscanf 函数
二者叫格式化读写函数。其作用与 printf 和 scanf 函数相当 ,只是后者以终端(显示器,键盘)为对象。前者是以磁盘文件为读写对象。
格式: fprintf( fp, 格式字符串, 输出表列);
fscanf( fp, 格式字符串, 输入表列);
如:
int i=3;
float t=4.5;
fprintf ( fp,“%d,%6.2f ”, i, t );
则是将字符串 3 , 4.5 写到 fp 指向的磁盘文件中.
同样:
fscanf (fp,“%d,%f”, &i, &t );
说明:
用 fprintf 和 fscanf 函数对磁盘文件读写, 输入时要将ASCII码转换为二进制形式。在输出时又要将二进制形式转换为字符,因此比较费时。不利于内存与磁盘间频繁交换数据。
7.其它读写函数
1.putw 和 getw 函数
作用: 从指定的磁盘文件中读写一个字(整数)
如: putw(10,fp);
i =getw(fp);
2. fgets 和 fputs 函数
作用: 从指定的磁盘文件中读写一个字符串.
如: char str[100];
fgets ( str,n,fp);
即从 fp 指向的文件中读入n-1个字符,并放入数组 str
中。如在读入 ( n-1) 个字符结束之前遇到换行符或EOF,
读入即结束.
同样: fputs(“China”,fp);
或者: fputs(str,fp);
三 文件的定位
在顺序读写文件时,文件中的位置指针是自动变化的,即读写完一个字符后,该位置指针自动移向下一个字符位置.可以用函数来强制是位置指针指向其它位置。
1. rewind 函数
作用: 使文件位置指针重返文件的开头.此函数无返回值.
格式: rewind(fp);
main ( )
{
FILE *fp1 , *fp2 ;
fp1=fopen( “file1.c ”, “r”);
fp2=fopen( “file2.c ”, “w”);
while( !feof (fp1)) putchar(fgetc(fp1));
rewind( fp1);
while( !feof(fp1)) fputc(fgetc(fp1),fp2);
fclose(fp1);
fclose(fp2);
}
2. fseek 函数和随机读写
顺序读写: 如果文件位置指针是按字节位置顺序移动。
随机读写: 如果文件位置指针能按需要移动到任意位置。
格式: fseek ( 文件类型指针 , 位移量 , 起始点 )
文件开始 SEEK_SET 用 0 表示.
起始点: 文件当前位置 SEEK_CUR 用 1 表示
文件末尾 SEEK_END 用 2 表示
位移量: 起始点为基点,向前移动的字节数。要求为长整型数据,以使文件大于 64 k 时不会出问题.
如: fseek ( fp , 100L , 0 )
fseek ( fp , 50L , 1 )
fseek ( fp , -10L , 2 )
说明: fseek 函数一般多用于二进制文件。对于文本文件,由于存在字符转换问题,因此位置计算可能引起混乱。
/*例 13.5 磁盘文件上有4个学生的数据,将 1, 3 个学生数据输入计算机,并在屏幕上显示出来.*/
代码段:
void test13_7()
{
int i;
FILE *fp;
if((fp=fopen("stulist","rb"))==NULL)
{
printf("Cannot open this file\n");
exit(0);
}
for(i=0;i<4;i+=2)
{
fseek(fp,i*sizeof(struct student),0);
fread(&stu[i],sizeof(struct student),1,fp);
printf("%s %d %d %d",stu[i].name,stu[i].num,stu[i].year,stu[i].score);
printf("\n");
}
fclose(fp);
printf("\n");
}
测试及分析:
3. ftell 函数
作用: 获取文件位置指针的当前位置(用相对于文件开头的位移量来表示,单位是 字节 )
格式: ftell(fp);
正常情况下 ftell 函数的返回值就是位置指针的位置。如返回值为-1L , 则表示出错.
main( )
{
long int i;
FILE *fp;
fp = fopen ( “stud_dat”, “ rb ”);
……
i = ftell ( fp );
if ( i = = -1L )
printf(“error \n”);
else
printf(“%ld ”,i );
…….
}
四 出错的检测
1. ferror 函数
调用各种输入,输出函数(如: putc , getc , freed , fwrite等)时,函数返回值可以反映出是否出错。也可以用ferror函数来检测。
格式: ferror( fp );
如ferror的返回值为0(假),则表示未出错。反之,则出错。
说明:
1. 在执行 fopen 函数时 , ferror 函数的值自动置为 0 。
2. 对同一文件,每一次调用输入或输出函数,均产生一个新的 ferror 函数值。因此,在调用一个输入或输出函数后。应立即检测 ferror 函数值。否则,信息会丢失.
fputc(ch,fp);
if( ferror( fp )!=0);
printf(“error \n”);
…….
fgetc(fp);
if (ferror( fp )!=0);
2. clearerr 函数
格式: clearerr(fp);
作用: 使文件错误标志和文件结束标志置为 0 。
在调用一个输入或输出函数时如出错,ferror 函数的值为非零值,如此时调用clearerr 函数,则 ferror 函数的值变成 0 。
另外: 只要出现错误标志,就一直保留,直到对同一文件调用了:
(1) clearerr 函数或 rewind 函数。
(2) 任何其它一个输入输出函数。
如: putc , getc , freed , fwrite 等.