我理解f中的ftell()和fseek()的工作方式,但对于这个问题,我无法在任何地方找到任何精确的答案,包括来自StackOverflow(LINK)上最近的帖子。
那么请你回答以下问题:
该函数用pos指向的fpos_t对象填充流的位置指示器所需的信息,以将流恢复到当前位置
fgetpos()
和fsetpos()
与文本和二进制模式都相关。
fgetpos()
的优势在于它保持流中的完整位置,包括其内部状态,以便您可以稍后恢复。无论您是否处于文本模式,这都有效。如果您在同一文件中使用wide oriented streams或混合fgetc()
和fgetwc()
,这一点尤其重要,因为某些语言环境使用依赖于状态的多字节编码(状态取决于先前的读取)。
fseek()
和ftell()
也可以使用文本和二进制模式。但是在文本模式中有一个重要的限制:你应该只使用fseek()
为0或ftell()
先前返回的值(在二进制模式下你可以使用你想要的任何值)。这是因为文本模式读取可以改变从读取返回的字节数与文件中有效字节的比较(典型示例,Windows文件中的2个CR + LF字节被转换为signe LF字节)。
由于ftell()
只返回long int
偏移量,因此如果需要,它无法跟踪多字节状态。所以使用fseek()
可能会失去这种状态。
不完全的。从Beej可以找到线索:
几乎在每个系统(当然还有我所知道的每个系统)上,人们都不使用这些函数,而是使用ftell()和fseek()。存在这些函数以防您的系统无法将文件位置记为简单的字节偏移。
在某些非UNIX系统上,fpos_t对象可能是一个复杂的对象,这些例程可能是可移植地重新定位文本流的唯一方法。
在Windows:
它假定缓冲区中的任何\ n字符最初是一个\ r \ n序列,当数据被读入缓冲区时,该序列已被规范化。
也就是说,在文本模式下打开时,Windows中没有(Windows-linebreak)文本文件的文件出错,因为fsetpos
假设该文件确实是(Windows-linebreak)文本文件,因此不能包含\n
\r
。
C11标准说(我的重点):
7.21.2/6:
每个面向广泛的流都有一个关联的mbstate_t对象,该对象存储流的当前解析状态。成功调用fgetpos会将此mbstate_t对象的值的表示存储为fpos_t对象的值的一部分。稍后使用相同的存储fpos_t值成功调用fsetpos会恢复关联的mbstate_t对象的值以及受控流中的位置。
请注意,fseek
和ftell
对mbstate_t
对象没有任何说法:他们不会报告或恢复它。因此,在面向广泛的流(也就是说,您使用了面向广泛的I / O函数的流)上,它们只重置文件位置,而不是(如果实现实际上有多个mbstate_t
对象的可能值) )整个流的状态。
面向广播的流与文本流不同,只是读取宽文本文件是它们的常用用途。实际上,fseek
和ftell
被记录为能够重置文本文件上的文件位置,前提是您正确使用它们。所以我相信(我可能错了)只有在流上使用宽I / O功能时才需要fsetpos
和fgetpos
。
除了其他答案中提到的原因外,如果您使用的是非常大的文件,包含超过fgetpos
字节的文件,可能需要使用fsetpos
和LONG_MAX
。在LONG_MAX
为231 - 1的系统中,这是一个真正的问题;现在,文件中包含超过20亿个字节的文件并不常见。
如果您使用的是实现POSIX.1-2001的系统,那么在包含任何系统头文件之前,有一个更好的选择,即#define _FILE_OFFSET_BITS 64
,然后使用fseeko
and ftello
。这些就像fseek
和ftell
一样,除了他们接受/返回off_t
数量,如果你已经制作了上面的#define
,保证是一个整数类型,可以代表263 - 1,这对任何人都应该足够了。这样更好,因为你可以对off_t
进行算术运算;你不能使用fpos_t
去你尚未去过的地方。但如果您不在POSIX系统上,fgetpos
和fsetpos
可能是您唯一的选择。
(请注意,某些系统会为您提供一个fpos_t
,它不能代表大于LONG_MAX
字节的文件偏移量。在其中一些系统中,应用相同的#define _FILE_OFFSET_BITS 64
设置会有所帮助。在其他系统中,如果你想要的话,你就完全没有运气了一个巨大的文件。)