C\C++|理解文件读写的关键:缓冲区与文件内位置指针

程序的输入输出都会抽象成文件(字节流)的形式。这里所指的文件是指除了标准文件(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-

原文链接:,转发请注明来源!