fgetpos/fsetpos和ftell/fseek之间有什么区别?

44

fgetpos()fsetpos() 与使用 ftell()fseek() 函数来获取和设置文件位置的区别在哪里?

fgetpos()fsetpos() 有什么作用?为什么会使用它们而不是使用 ftell()fseek() 呢?


1
请参考https://dev59.com/BWct5IYBdhLWcg3wZcqL,它的答案比这里被接受的答案好得多。 - Mecki
4个回答

29
以上答案均不正确 - 实际上,如果您将fsetposfseek互换使用,则可能会引入安全漏洞(https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=20087255)。
原因是fsetposfpos_t *pos参数实际上不是整数,因此不能用于在文件中寻找任意位置。因此,唯一有效的值是从fgetpos获得的值。正如文档所述,

与流相关联的内部文件位置指示器设置为由pos表示的位置,其中pos是指向fpos_t对象的指针,其值先前应通过调用fgetpos获得。

http://www.cplusplus.com/reference/cstdio/fsetpos/
如果您只想要能够定位到超过32位边界的任意位置,则使用ftello / fseeko并使用#define _FILE_OFFSET_BITS 64进行编译即可。

1
不是确切的答案,但作为提示非常好!谢谢! - likern
1
@likern 这正是问题的答案。而且这也是正确的答案,被接受的答案完全不正确,因为 fpos_t 可能根本不是整数(标准并不要求它是整数)。 - Mecki
ftellofseeko是POSIX函数。此外,C11标准在7.21.9.1中指出:“如果有的话,fgetpos函数将流stream所指向的解析状态和文件位置指示器的当前值存储在pos所指向的对象中。”此外,在7.21.1中指出:“fpos_t是一个完整的对象类型,而不是数组类型,能够记录指定文件中每个位置所需的所有信息。”然而,在我的系统上,fpos_t至少是一个__darwin_off_t,它实际上是一个__int64_t。尽可能使用fsetpos和fgetpos。 - math
1
我无法打开链接 :/ 有推理的副本吗? - Flamefire
我不会说这是错的,因为我不知道,但每次我从Confluence上寻求潜在的C语言安全风险建议时,他们都是错误的。 - Hunter Kohler

18

根据 man 手册,我们可以看到 ftell 和 fseek 使用 long int 类型来表示文件中的偏移量(位置),因此可能会受限于 long int 可以表示的偏移量范围(long int 类型不能保证能够保存大于 2^31-1 的值,最大偏移量限制为 2GB)。然而,新版的 fgetpos 和 fsetpos 函数使用了一个特殊的 typedef,fpos_t,来表示偏移量。如果选择恰当的类型,该 typedef 背后的类型可以表示任意大小的偏移量,因此 fgetpos 和 fsetpos 可以用于处理任意巨大的文件。此外,fgetpos 和 fsetpos 还记录与多字节流相关联的状态。


9
这个答案是不正确的!如果你只是想寻求2 ** 31-1以外的位置,那么可以使用fseeko。正确的答案是fgetpos只能定位到先前由fgetpos获取的位置,而且不能将fpos_t解释为整数。正如手册中所说:“在某些非UNIX系统上,fpos_t对象可能是一个复杂的对象,这些例程可能是可移植重新定位文本流的唯一方法。” - Mecki
@Mecki 是的,确实,这个答案是错误的。就我所知道的,下面的回答是唯一正确的。 - HughHughTeotl
5
答案并不是错误的,但还不够充分。它正确地说明了 fseek 和相关函数的限制,并正确地指出 fgetpos 和相关函数是没有这些限制的选择。上面的答案从未说明过可以混合使用 fseekfsetpos 及其相关函数,这是确实危险的。此外,它也从未说明 fpos_t 是一个整数类型。相反,Mecki 建议使用非 ISO-C 标准函数(但是 POSIX 函数) fseeko 和相关函数。 - math

6

fgetpos()与fsetpos()配对使用,而ftell()则与fseek()搭配使用。

fgetpos()和ftell()看起来很相似,因为两者都需要FILE*类型的参数,并且都返回文件中的某种位置,尽管风格稍有不同。但是请注意:只有fseek()允许您从文件开头、当前位置和文件末尾向后搜索(fseek()的第三个参数分别为SEEK_SET、SEEK_CUR和SEEK_END)。fsetpos()无法实现这一点。fsetpos()仅限于返回您从fgetpos()获取的某个位置。

假设你想要将文件读入内存。你需要从malloc()获取多少堆内存?你需要知道文件的大小。我还没有发现C标准库中有任何函数可以告诉您文件的大小,尽管某些C编译器可能会添加一个非标准函数。例如,Borland Turbo C拥有filelength()函数。但我在标准C库中没有看到filelength()函数。

所以,你可以使用fseek()函数将指针定位到文件结尾前的0字节处。然后使用ftell()获取字节位置,并按照此计算出所需的堆内存量。

还有其他方法可以获取文件大小吗?您可以使用fgetc()函数读取每个字符,计数直到达到文件末尾。但我认为使用fseek()和ftell()更好、更容易。


-3

在功能上没有区别,尽管接口定义不同。它们都被实现,因为它们都是 POSIX 的一部分。


3
这两个接口都来自于ISO C,而非POSIX。当文件变得非常大时,它们之间存在重大的功能差异。 - zwol
它们的区别在于它们寻址位置的方式:可以通过 long intfpos_t - math

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接