C Primer Plus(6) 中文版 第13章 文件输入/输出 13.5 随机访问:fseek()和ftell()

13.5 随机访问:fseek()和ftell()
有了fseek()函数,便可把文件看作是数组,在fopen()打开的文件中直接移动到任意字节处。
程序清单13.4演示fseek()和ftell()的用法。注意,fseek()有3个参数,返回int类型的值;ftell()函数返回一个long类型的值,表示文件中的当前位置。
/* reverse.c -- displays a file in reverse order */
#include <stdio.h>
#include <stdlib.h>
#define CNTL_Z '\032'   /* eof marker in DOS text files */
#define SLEN 81
int main(void)
{
    char file[SLEN];
    char ch;
    FILE *fp;
    long count, last;
    
    puts("Enter the name of the file to be processed:");
    scanf("%80s", file);
    if ((fp = fopen(file,"rb")) == NULL)
    {                               /* read-only mode   */
        printf("reverse can't open %s\n", file);
        exit(EXIT_FAILURE);
    }
    
    fseek(fp, 0L, SEEK_END);        /* go to end of file */
    last = ftell(fp);
    for (count = 1L; count <= last; count++)
    {
        fseek(fp, -count, SEEK_END); /* go backward      */
        ch = getc(fp);
        if (ch != CNTL_Z && ch != '\r')  /* MS-DOS files */
            putchar(ch);
    }
    putchar('\n');
    fclose(fp);
    
    return 0;

/* 输出:

*/ 

该程序使用二进制模式,以便处理MS-DOS文本和UNIX文件。但是,在使用其他格式文本文件的环境总可能无法正常运行。
注意
如果通过命令行环境运行该程序,待处理文件要和可执行文件在同一个目录(或文件夹)中。如果在IDE中运行该程序,具体查找方法因实现而异。
13.5.1 fseek()和ftell()的工作原理
fseek()的第1个参数是FILE指针,指向待查找的文件,fopen()应该已打开该文件。
fseek()的第2个参数是偏移量(offset)。该参数表示从起始点开始要移动的距离(参见表13.3列出的起始点模式)。该参数必须是一个long类型的值,可以为正(前移)、负(后移)或0(保持不动)。
fseek()的第3个参数是模式,该参数确定起始点。根据ANSI标准,在stdio.h头文件中规定了几个表示模式的明示常量(manifest constant),
如表13.3 所示。
                表13.3 文件的起始点模式
模式            偏移量的起始点
SEEK_SET        文件开始处
SEEK_CUR        当前位置
SEEK_END        文件末尾
旧的实现可能缺少这些定义,可以使用数值0L、1L、2L分别表示这3种模式。L后缀表明其值是long类型。或者,实现可能把这些明示常量定义在别的头文件中。如果不确定,请查阅实现的使用手册或在线帮助。
下面是调用fseek()函数的一些示例,fp是一个文件指针:
fseek( fp, 0L, SEEK_SET ); //定位至文件开始处
fseek( fp, 10L, SEEK_SET ); //定位至文件中的第10个字节
fseek( fp, 2L, SEEK_CUR ); //从当前位置前移2个字节
fseek( fp, 0L, SEEK_END ); //定位至文件结尾
fseek( fp, -10L, SEEK_END ); //从文件结尾处回退10个字节
如果一切正常,fseek()的返回值为0;如果出现错误(如试图移动的距离超出文件的范围),其返回值为-1。
ftell()函数的返回类型是long,它返回的是参数指向文件的当前位置距文件开始处的字节数。ANSI C把它定义在stdio.h中。在最初实现的UNIX中,ftell()通过返回距文件开始处的字节数来确定文件的位置。文件的第1个字节到文件开始处的距离是0,以此类推。ANSI C规定,该定义适用于以二进制模式打开的文件,以文本模式打开的情况不同。
fseek( fp, 0L, SEEK_END );
把当前位置设置为距文件末尾0字节的偏移量。也就是说,该语句把当前位置设置在文件结尾。
下一条
last = ftell( fp );
把从文件开始处到文件结尾的字节数赋给last。 
for (count = 1L; count <= last; count++)
{
    fseek(fp, -count, SEEK_END); /* go backward      */
    ch = getc(fp);
}
第1轮迭代,把程序定位到文件结尾的第1个字符(即,文件的最后一个字符)。然后,程序打印该字符。下一轮迭代把程序定位到前一个字符,并打印该字符。重复这一过程直至到达文件的第1个字符,并打印。
13.5.2 二进制模式和文本模式
UNIX只有一种文件格式,所以不需要进行特殊的转换。然而MS-DOS要格外注意。许多MS-DOS编译器都用Ctrl+Z标记文本文件的结尾。以文本模式打开这样的文件时,C能识别这个作为文件结尾标记的字符。但是,以二进制模式打开相同的文件时,Ctrl+Z字符被看作是文件中的一个字符,而实际的文件结尾符在该文件的后面。文件结尾符可能紧跟在Ctrl+Z字符后面,或者文件中可能用空字符填充,是该文件的大小是256的倍数。在DOS环境下不会打印空字符,reverse.c就包含了防止打印Ctrl+Z字符的代码。 
二进制模式和文本模式的另一个不同之处是:MS-DOS用\r\n组合表示文本文件换行。以文本模式打开相同的文件时,C程序把\r\n“看成”\n。
但是,以二进制模式打开该文件时,程序能看见这两个字符,因此,reverse.c中还包含了不打印\r的代码。通常,UNIX文本文件既没有Ctrl+Z,也没有\r,所以这部分代码不会影响大部分UNIX文本文件。
ftell()函数在文本模式和二进制模式中的工作方式不同。许多系统的文本文件格式与UNIX的模型有很大不同,导致从文件开始处统计的字节数成为一个毫无意义的值。ANSI C规定,对于文本模式,ftell()返回的值可以作为fseek()的第2个参数。对于MS-DOS,ftell()返回的值把\r\n当做一个字节计数。
13.5.3 可移植性
理论上,fseek()和ftell()应该复合UNIX模型。但是,不同系统存在着差异,有时确实无法做到与UNIX模型一致。因此,ANSI对这些函数降低了要求。下面是一些限制。
*在二进制模式中,实现不必支持SEEK_END模式。因此无法保证程序reverse.c的可移植性。移植性更高的方式是逐字节读取整个文件直到文件末尾。C预处理器的条件编译指令(第16章介绍)提供了一种系统方法来处理这种情况。
*在文本模式中,只有以下调用能保证其相应的行为。
函数调用                            效果
fseek( file, 0L, SEEK_SET )            定位至文件开始处
fseek( file, 0L, SEEK_CUR )            保持当前位置不动
fseek( file, 0L, SEEK_END )            定位至文件结尾
fseek( file, ftell-pos, SEEK_SET )    ftell-pos是ftell()的返回值
不过,许多常见的环境都支持更多的行为。
13.5.4 fgetpos()和fsetpos()函数
fseek()和ftell()潜在的问题是,它们都把文件大小限制在long类型能表示的范围内。鉴于此,ANSI C新增了两个处理较大文件的新定位
函数:fgetpos()和fsetpos()。这两个函数不使用long类型的值表示位置,它们使用一种新类型:fpos_t(代表file position type,文件定位类型)。fpos_t类型不是基本类型,它根据其他类型来定义。fpos_t类型的变量或数据对象可以在文件中指定一个位置,它不能是数组类型,除此之外,没有其他限制。实现可以提供一个满足特殊平台要求的类型,例如,fpos_t可以实现为结构。
ANSI C定义了如何使用fpos_t类型。fgetpos()函数的原型如下:
int fgetpos( FILE *restrict stream, fpos_t *restrict pos );
调用该函数时,它把fpos_t类型的值放在pos指向的位置上,该值描述了文件中的当前位置距文件开头的字节数。如果成功,fgetpos()函数返回0;如果失败,返回非0。
fsetpos()函数的原型如下:
int fsetpos( FILE *stream, const fpos_t *pos );
调用该函数时,使用pos指向位置上的fpos_t类型值来设置文件指针指向偏移该值后指定的位置。如果成功,fsetpos()函数返回0;如果失败,则返回非0。fpos_t类型的值应通过之前调用fgetpos()获得。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_40186813

你的能量无可限量。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值