C Primer Plus 第13章 文件输入/输出 13..5 随机存取fseek()和ftell()

13.5  随机存取:fseek()和ftell()函数

Fseek()允许您像对待数组那样对待一个文件,在fopen()打开的文件中直接移动到任意字节处。

我们创建程序清单13.5中的程序,该程序按反序显示一个文件。像前面的例子一样,它使用一个命令行参数来获得要读取的文件的名字。请注意fseek()接受3个参数,返回一个int值 。ftell()函数以一个long类型值返回一个文档的当前位置。

程序清单13.5  reverse.c程序

/*reverse.c—反序显示一个文件*/
#include <stdio.h>
#include <stdlib.h>
#define CNTL_Z '\032'    /*DOS文本文件中的文件结尾标记*/
#define SLEN 50
int main(void)
{
    char file[SLEN];
    char ch;
    FILE * fp;
    long count,last;

    puts("Enter the name of the file to be processed: ");
    gets(file);
    if((fp=fopen(file,"rb"))==NULL)    /*只读和二进制模式*/
    {
        printf("reverse can not open %s\n",file);
        exit(1);
    }
    fseek(fp,0L,SEEK_END);    /*定位到文件结尾处*/
    last=ftell(fp);
    for(count=1L;count<=last;count++)
    {
        fseek(fp,-count,SEEK_END);  /*回退*/
        ch=getc(fp);
        /*针对 DOS,在UNIX下也可以工作*/
        if(ch!=CNTL_Z && ch!= '\r')
            putchar(ch);
        /*针对 MACINTOSH  */
        /*if(ch=='\r')
        putchar ('\n');
        else
            putchar(ch);  */
    }
    putchar('\n');
    fclose(fp);
    return 0;
}



我们需要讨论3个问题:fseek()和ftell()如何工作,如何使用二进制流,如何使程序可移植。

13.5.1  fseek()和ftell()如何工作

在fseek()的三个参数中,第一个参数是指向被搜索文件的FILE指针应该已经使用fopen()打开了该文件。

fseek()的第二个参数称为偏移量(offset),表示从起点开始要移动的距离(请参见表13.3中的起点模式),这个参数必须是一个long类型的值,可以为正(前移)、负(后移),也可以为零。

第三个参数是模式,用来标识起识点。在ANSI下,stdio.h头文件指定了下列模式常量:

模式

偏移量的起始点

SEEK_SET

文件开始

SEEK_CUR

当前位置

SEEK_END

文件结尾

下面是函数骼的一些例子:

fseek(fp,0L,SEEK_SET);    //找到文件的开始处

fseek(fp,10L,SEEK_SET);    //找到文件的第10个字节

fseek(fp,2L,SEEK_CUR);    //从文件的当前位置向前移动两个字节

fseek(fp,0L,SEEK_END);    //文件结尾

如果一切正常,fseek()的返回值为0,如果出现错误,例如试图移动超出文件范围,则fseek()的返回值为-1.

ftell()函数为long类型,它返回文件的当前位置。在ANSI下,ftell()函数在stdio.h头文件中被声明。ftell()函数通过返回距文件开始处的字节数目来确定文件的位置。文件的第一个字节到文件开始处的字节数为0,依此类推。在ANSI C 下这种定义适用于以二进制模式打开的文件,但是对于以文本模式打开的文件来讲,不一定是这样。这也是程序清单13.5使用二进制模式的原因之一。现在我们来看程序清单13.5的基本元素。

首先,以下语句:

fseek(fp,0L,SEEK_END);

把当前位置设置为从文件结尾处偏移0字节处,也就是将位置设定在文件结尾。接下来,

last=ftell(fp);

把从文件开始到文件结尾处的字节数目赋给last。接下来是一个循环:

for(count=1L;count<=last;count++)

{

    fseek(fp,-count,SEEK_END);

    ch=getc(fp);

}

第一次循环将程序定位到文件结尾前的第一个字符,也即文件的最后一个字符。然后打印这个字符。下一次循环,将程序定位到前一个字符并打印之。这种操作会一直持续到到达第一个字符并打印之。

13.5.2  二进制模式和文本模式

我们把程序清单13.5的程序设计为在UNIX和MS_DOS环境下都可以运行。UNIX只有一种文件格式,所以不需要特殊的调整。可是MS_DOS即需要额外的关注。很多MS_DOS编辑器使用字符 ctrl+z标识文本文件的结尾。如果以文本模式打开这样的文件,C可以认出这个字符是标识文件结尾的字符。可是以二进制模式打开这样的文件,只会把ctrl+z当作文件中的一个字符。真正的文件结尾还在后面,也许紧跟着ctrl+z也许用空字符填补文件以使它的大小为256的倍数。在DOS模式下不打印空字符。程序中包含了防止程序打印ctrl+z字符的代码。

另一个区别 ,DOS使用\r\n组合来表示文本文件的换行符。以文本模式打开的c程序将\r\n看作\n。但是使用二进制模式打开相同的文件,程序将看到这两个字符。所以需要在程序中包含防止打印\r的代码

因数UNIX文本文件通常不包含ctrl+z和\r,所以这段额外的代码不会影响大多数的UNIX文本文件。

函数ftell()在文本模式和二进制模式下的工作方式有所不同。很多系统的文本文件格式都与UNIX模型有很大不同,以致从文件开始的字节计数值是无意义的数量。ANSI C规定,对于文本模式,ftell()返回一个可以用作fseek()第二个参数的值。例如,对于DOS,ftell()返回一个将\r\n看成一个字节的计数值 。

13.5.3  可移植性

理论上,fseek()和ftell()应该符合UNIX模型。可是由于实际系统之间的差异,有时这是不可能的。所以,ANSI降低了对这些函数的要求。下面是一些局限性:

*对于二进制模式,C实现不需要支持SEEK_END模式。这样就无法保证程序清单13.5是可移植的。然而,程序清单并没有给出定位文件结尾的可替代方法。因为可替代方法是顺序地读取整个文件以找到文件末尾,这比简单地跳到文件末尾要慢得多。第16章中将要讨论的C预处理器条件编译指令,为处理可替代的代码提供了一种更为系统的方法。

*在文本模式中,可以确保有效的fseek()调用只有以下这些:

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 引入了两个用来处理较大文件的新的定位函数。这两个函数不是采用long类型值 ,而是采用一种称为fpos_t(代表file position type,文件定位类型)的新类型来代表位置。fpos_t 不是一种基本类型,而是通过其他类型定义的。一个fpos_t类型的变量或者数据对象可以用来指定文件中的一个位置。它不能是一种数组类型,但除此之外不再有其他限制 。因此,C实现可以提供一种满足特殊平台需要的类型;例如,这种类型可以作为结构来实现 。

ANSI C 定义了使用fpos_t的方法。fgetpos()函数具有下面的原型:

int fgetpos(FILE * restrict stream,fpos_t * restrict pos);

被调用时,该函数在pos所指的位置放置一个fpos_t值;这个值描述了文件的一个位置。如果成功函数返回0,否则返回一个非0值。

fsetpos()函数具有下面的原型:

int fsetpos(FILE * stream,const fpos_t *pos);

被调用时,该函数使用pos指向的位置上的那个fpos_t值设定文件指针指向该值所指示的位置。如果成功,函数返回0,否则返回一个非0值 。这个fpos_t值应该是通过调用fgetpos()来获取的。

转载于:https://my.oschina.net/idreamo/blog/838841

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值