博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C Primer Plus 第13章 文件输入/输出 13..5 随机存取fseek()和ftell()
阅读量:5847 次
发布时间:2019-06-19

本文共 3834 字,大约阅读时间需要 12 分钟。

hot3.png

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

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

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

程序清单13.5  reverse.c程序

/*reverse.c—反序显示一个文件*/#include 
#include
#define CNTL_Z '\032' /*DOS文本文件中的文件结尾标记*/#define SLEN 50int 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

你可能感兴趣的文章
HDOJ 1003:求一串数字中和最大的连续子串
查看>>
RedHat 5.6_x86_64 + ASM + RAW+ Oracle 10g RAC (二)
查看>>
win7不能全屏
查看>>
MySQL/InnoDB的并发插入Concurrent Insert
查看>>
产品经理有话说——产品汪成长记(入职)
查看>>
从魔兽世界到激战2看MMO网游角色成长
查看>>
转两好文防丢:Debian 版本升级/降级 & Linux 应用程序失去输入焦点问题的解决...
查看>>
HDU - Pseudoforest
查看>>
Nexus杂
查看>>
Android --- GreenDao的实现(ORM框架)
查看>>
js_coding
查看>>
Linux平台Java调用so库-JNI使用例子
查看>>
PCM数据格式,多少字节算一帧
查看>>
Spring Data JPA
查看>>
KACK的处理方法
查看>>
POJ3438 ZOJ2886 UVALive3822 Look and Say【数列】
查看>>
IE6的height小BUG
查看>>
说说IUnitOfWork~DbContext对象的创建应该向BLL层公开
查看>>
强制卸载kernel
查看>>
web渗透测试中WAF绕过讲解(二)基于HTTP协议绕过
查看>>