这篇文章中将会讨论到:fseek()
和ftell()
函数的工作原理、如何使用二进制流、如何让程序可移植。
有了fseek()
函数,便可把文件看作是数组,在fopen()
打开的文件中直接移动到任意字节处。我们创建一个程序reverse.c
演示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 %sn", 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;
}
下面是对一个文件的输出:
Enter the name of the file to be processed: Cluv .C ni eno naht ylevol erom margorp a ees reven llahs I taht kniht I
该程序使用二进制模式,以便处理MS-DOS
文本和UNIX
文件。但是,在使用其他格式文本文件的环境中可能无法正常工作。
如果通过命令行环境运行该程序,待处理文件要和可执行文件在同一个目录(或文件夹)中。如果在IDE中运行该程序,具体查找方案序因实现而异。例如,默认情况下,Microsoft Visual Studio 2012
在源代码所在的目录中查找,而Xcode 4.6
则在可执行文件所在的目录中查找。
1 fseek()和ftell()的工作原理
fseek()
的第1个参数是FILE
指针,指向待查找的文件,fopen()
应该已打开该文件。
fseek()
的第2个参数是偏移量(offset
)。该参数表示从起始点开始要移动的距离(参见表13.3列出的起始点模式)。该参数必须是一个long
类型的值,可以为正(前移)、负(后移)或0(保持不动)。
fseek()
的第3个参数是模式,该参数确定起始点。根据ANSI
标准,在stdio.h
头文件中规定了几个表示模式的明示常量(manifest constant
),如表13.3所示。
旧的实现可能缺少这些定义,可以使用数值0L、1L、2L
分别表示这3种模式。L
后缀表明其值是long
类型。或者,实现可能把这些明示常量定义在别的头文件中。如果不确定,请查阅实现的使用手册或在线帮助。
下面是调用fseek()
函数的一些示例,fp
是一个文件指针:
fseek(fp, 0L, SEEK_SET); // go to the beginning of the file
fseek(fp, 10L, SEEK_SET); // go 10 bytes into the file
fseek(fp, 2L, SEEK_CUR); // advance 2 bytes from the current position
fseek(fp, 0L, SEEK_END); // go to the end of the file
fseek(fp, -10L, SEEK_END); // back up 10 bytes from the end of the file
对于这些调用还有一些限制,我们稍后再讨论。
如果一切正常,fseek()
的返回值为0;如果出现错误(如试图移动的距离超出文件的范围),其返回值为-1。
ftell()
函数的返回类型是long
,它返回的是参数指向文件的当前位置距文件开始处的字节数。ANSI-C
把它定义在stdio.h
中。在最初实现的UNIX
中,ftell()
通过返回距文件开始处的字节数来确定文件的位置。文件的第1个字节到文件开始处的距离是0,以此类推。ANSI C
规定,该定义适用于以二进制模式打开的文件,以文本模式打开文件的情况不同。这也是程序reverse.c
以二进制模式打开文件的原因。
下面,我们来分析程序reverse.c
中的基本要素。首先,下面的语句:
fseek(fp, 0L, SEEK_END);
把当前位置设置为距文件末尾0字节偏移量。也就是说,该语句把当前位置设置在文件结尾。下一条语句:
last = ftell(fp);
把从文件开始处到文件结尾的字节数赋给last
。然后是一个for
循环:
for (count = 1L; count <= last; count++)
{
fseek(fp, -count, SEEK_END); /* go backward */
ch = getc(fp);
}
第1轮迭代,把程序定位到文件结尾的第1个字符(即,文件的最后一个字符)。然后,程序打印该字符。下一轮迭代把程序定位到前一个字符,并打印该字符。重复这一过程直至到达文件的第1个字符,并打印。
2 二进制模式和文本模式
我们设计的程序reverse.c
在UNIX
和MS-DOS
环境下都可以运行。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
当作一个字节计数。
3 可移植性
理论上,fseek()
和ftell()
应该符合UNIX
模型。但是,不同系统存在着差异,有时确实无法做到与UNIX
模型一致。因此,ANSI
对这些函数降低了要求。下面是一些限制。
- 在二进制模式中,实现不必支持
SEEK_END
模式。因此无法保证程序清单13.4的可移植性。移植性更高的方法是逐字节读取整个文件直到文件末尾。C预处理器的条件编译指令(第16章介绍)提供了一种系统方法来处理这种情况。
- 在文本模式中,只有以下调用能保证其相应的行为。
不过,许多常见的环境都支持更多的行为。
4 getpos()和fsetpos()函数
fseek()
和ftell()
潜在的问题是,它们都把文件大小限制在long
类型能表示的范围内。也许20亿字节看起来相当大,但是随着存储设备的容量迅猛增长,文件也越来越大。鉴于此,ANSI C
新增了两个处理较大文件的新定位函数:fgetpos()
和fsetpos()
。这两个函数不使用long
类型的值表示位置,它们使用一种新类型:fpos_t
(代表file positiontype,文件定位类型)。fpos_t
类型不是基本类型,它根据其他类型来定义。fpos_t
类型的变量或数据对象可以在文件中指定一个位置,它不能是数组类型,除此之外,没有其他限制。实现可以提供一个满足特殊平台要求的类型,例如,fpos_t
可以实现为结构。
ANSI-C
定义了如何使用fpos_t
类型。fgetpos()
函数的原型如下:
nt 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()
获得。
以上就是关于C
语言处理I/O
的两个随机访问函数:fseek()
和ftell()
的相关信息了,希望对大家有所帮助。想了解更多的知识,大家可以看一下教程
C教程:https://www.w3cschool.cn/c/
文章参考来源:www.toutiao.com/a6855410553498632708