对于文件的读写方式,C 语言不仅支持简单地顺序读写方式,还支持随机读写(即只要求读写文件中某一指定的部分)。对顺序读写方式来说,随机读写方式需要将文件内部的位置指针移动到需要读写的位置再进行读写,这通常也被称为文件的定位。
对于文件的定位,可以通过 rewind、fseek 与 ftell 函数来完成。
其中,rewind 函数用于将文件内部的位置指针重新指向一个流(数据流或者文件)的起始位置。这里需要注意的是,这里的“指针”表示的不是文件指针,而是文件内部的位置指针。即随着对文件的读写,文件的位置指针(指向当前读写字节)向后移动。而文件指针指向整个文件,如果不重新赋值,文件指针不会发生改变。
rewind 函数的一般原型如下所示:
从上面的函数原型可以看出,rewind 并没有返回值,因此也无法做安全性检查。如下面的示例代码所示:
FILE *fp=NULL;
fp=fopen("Test.txt","r");
if(fp==NULL)
{
}
rewind(fp);
在上面的示例代码中,由于 rewind 函数没有返回值,所以我们很难判断“rewind(fp)”是否执行成功。因此,应该尽量使用 fseek 来替换 rewind 函数,从而以验证流已经成功地回绕。如下面的示例代码所示:
if (fseek(fp, 0L, SEEK_SET) != 0)
{
}
相对于 rewind 函数而言,fseek 函数的功能更加强大,它用来设定文件的当前读写位置,从而可以实现以任意顺序访问文件的不同位置,以实现文件的随机访问。其函数的一般原型如下所示:
如果该函数执行成功,fp 将指向以 from 为基准,偏移 offset 个字节的位置,函数的返回值为 0;如果该函数执行失败(比如 offset 超过文件自身大小),则不改变 fp 指向的位置,函数的返回值为 -1,并设置 errno 的值,可以用 perror 函数来输出错误信息。
对于 fseek 函数中的参数:第一个参数 fp 为文件指针;第二个参数 offset 为偏移量,它表示要移动的字节数,整数表示正向偏移,负数表示负向偏移;第三个参数 from 表示设定从文件的哪里开始偏移,取值范围如表 1 所示。
起始点 | 表不符号 | 数字表示 |
---|---|---|
文件首 | SEEK_SET | 0 |
当前位置 | SEEK_CUR | 1 |
文件末尾 | SEEK_END | 2 |
由表 1 可知:
当 from 值为 SEEK_CUR 或 SEEK_END 时,参数 offset 允许出现负值。如下面的示例代码所示:
/*将读写位置移动到离文件开头100字节处*/
fseek(fp,100L,0);
/*将读写位置移动到离文件当前位置100字节处*/
fseek(fp,100L,1);
/*将读写位置退回到离文件结尾100字节处*/
fseek(fp,-100L,2);
/*将读写位置移动到文件的起始位置*/
fseek(fp,0L,SEEK_SET);
/*将读写位置移动到文件尾*/
fseek(fp,0L,SEEK_END);
不难发现,上面的语句“(void)fseek(fp,0L,SEEK_SET);”的作用实际上等同于 rewind 函数。与此同时,在使用 fseek 函数时,还应该注意如下 3 点。
ftell 函数的原型为:
该函数用于得到文件位置指针当前位置相对于文件首的偏移字节数。在随机方式存取文件时,由于文件位置频繁前后移动,程序不容易确定文件的当前位置。在使用 fseek 函数后,再调用函数 ftell 就能非常容易地确定文件的当前位置。如下面的示例代码所示:
long getfilelength(FILE *fp)
{
long curpos=0L;
long length=0L;
curpos = ftell(fp);
fseek(fp, 0L, SEEK_END);
length = ftell(fp);
fseek(fp, curpos, SEEK_SET);
return length;
}