程序的输入输出都会抽象成文件(字节流)的形式。这里所指的文件是指除了标准文件(stdin(如键盘)、stdout(如鼠标))之外的磁盘文件。
与文件相关联的每个流都有一个FILE类型的控制结构,定义有关文件操作的信息,可以访问它所执行的文件,进而对其进行读写操作。
struct _iobuf {
char *_ptr; // 文件输入的下一个位置
int _cnt; // 当前缓冲区的相对位置
char *_base; // 文件的起始位
int _flag; // 文件标志
int _file; // 有效性验证
int _charbuf; // 缓冲区的检査,若无此成员,则不读取
int _bufsiz; // 文件大小
char *_tmpfname; // 临时文件名
};
typedef struct _iobuf FILE;
对于文件的操作,一般有以下4个步骤:
1) 定义文件指针;
2) 打开文件;
3) 读写文件;
4) 关闭文件。
理解文件操作的缓冲区和文件内的位置指针的一些细节很重要。
1 文件指针和缓冲区
fopen()成功打开文件后,将返同文件指针(file pointer),其他I/O函数可以使用这个指针指定该文件。文件指针(如定义为fp)的类型是指向FILE的指针,FILE是一个定义在stdio.h中的派生类型。文件指针fp并不指向实际的文件,它指向一个包含文件信息的数据对象,其中包含操作文件的I/O函数所需的缓冲区信息。因为标准库中的I/O函数使用缓冲区,所以它们不仅要知道缓冲区的位置,还要知道缓冲区被填充的程度以及操作哪一个文件。C标准I/O的数据根据这些信息在必要时决定再次填充或清空缓冲区。
fopen()函数不仅打开一个文件,还创建了一个缓冲区(在读写模式下会创建两个缓冲区)以及一个包含文件和缓冲区数据的结构。另外,fopen()返回一个指向该结构的指针,以便其他函数知道如何找到该结构。假设把该指针赋给一个指针变量fp,我们说fopen()函数“打开一个流”。如果以文本模式打开该文件,就获得一个文本流;如果以二进制模式打开该文件,就获得一个二进制流。
当输入函数发现已读完缓冲区中的所有字符时,会请求把下一个缓冲大小的数据块从文件拷贝到该缓冲区中。以这种方式,输入函数可以读取文件中的所有内容,直到文件结尾。函数在读取缓冲区中的最后一个字符后,把结尾指示器设置为真。于是,下一次被调用的输入函数将返回EOF。
输出函数以类似的方式把数据写入缓冲区。当缓冲区被填满时,数据将被拷贝至文件中。
fflush()可以将缓冲区的内容强制刷新到文件。
fclose()可以把遗留在缓冲区中的数据写入文件,实施操作系统级的关闭操作。同时,释放与流联系的文件控制块,以后可以重复使用这部分空间。
int fclose(FILE *fp);
多数情况下,系统限制同时处于打开状态的文件总数,因此,打开文件前先关闭无用文件是必要的
fclose函数的返回值:
当顺利地执行了关闭操作,返回值为0。
如果返回值为非零值,表示关闭时有错误。
一般只有驱动器中无盘或盘空间不够时才失败,关闭失败会引起数据丢失、文件的破坏和程序中的随机错误。
对于新手来说,如果忘记写fclose()或fflush(),一些写入操作的数据可能不会写入文件,所以结对编程(写了fopen()即写flcose(),写了malloc()即写free(),写了GetDC()即写ReleaseDC)的习惯很重要。
2 文件内的位置指针
这个FILE结构通常包含一个指定流中当前位置的静态的文件位置指针。除此之外,FILE结构还包含错误和文件结尾的指示器、一个指向缓冲区开始处的指针、一个文件标识符和一个计数(统计实际拷贝进缓冲区的字节数)。
在初始化结构和缓冲区后, 输入函数按要求从缓冲区中读取数据。在它读取数据时, 文件位置指示器被设置为指向刚读取字符的下一个字符。由于stdio.h系列的所有输入函数都使用相同的缓冲区,所以调用任何一个函数都将从上一次函数停止调用的位置开始。
一些读函数,如getc(),会在读到文件末尾时在文件流中设置EOF:
#include <stdio.h>
int main ( )
{
FILE *fpin,*fpout;
char b=0x34;
char d=0xff;
fpin =fopen ("dat1.dat","wb");
fputc(b,fpin);
fputc(d,fpin);
fputc(b+22,fpin);
fclose ( fpin );
fpin =fopen ("dat1.dat","rb");
char c;
#if 0
while(!feof(fpin))
{
c = getc(fpin); // getc()函数根据读入情况为设置EOF
printf("%c\n", c); // 会输出EOF
}
#elif 0
c = getc(fpin);
while(c!=EOF) // 二进制文件中可能包含有-1(EOF=0xff)
{
printf("%c\n", c);
c = getc(fpin);
}
#else
c = getc(fpin);
while(!feof(fpin)) // 此前已有调用getc()
{
printf("%c\n", c);
c = getc(fpin);
}
#endif
fclose ( fpin );
while(1);
return 0;
}
3 操作文件位置指针的相关函数
如果一个文件只能进行顺序存取操作,则称为顺序文件。典型的顺序文件是键盘、显示器和保存在磁带上的文件。
如果一个文件可以在文件的任意位置进行存取操作,则称为随机文件。磁盘文件就是典型的随机文件。
fgetpos()、fseek()、fsetpos()这三个函数可以实现对文件读取中位置的操作。
Append 方式打开,文件指针在文件尾部,其它方式打开时,文件指针指向文件的开始位置。
操作过程中想让文件指针重新指到文件开始位置,用 rewind(fp)即可。
用fseek ( fp , 0 , SEEK_SET ); 也可。
类istream中与位置指针相关的函数如下:
(1)移动读指针函数:
a、istream & seekg(long pos);
功能是将读指针设置为pos,即将读指针移动到文件的pos字节处。
b、istream & seekg(long offset,ios::seek_dir dir);
功能是将读指针按照seek_dir的指示(方式)移动offset个字节,其中seek_dir是在类ios中定义的一个枚举类型。
(2)返回读指针当前位置值的函数
long tellg();
函数返回值为流中读指针的当前位置。
-End-