fgetpos() 和 fsetpos() 仅适用于文本模式吗?如果不是字节数,fpos_t 对象填充的是什么 location/offset 数据?

Are fgetpos() and fsetpos() only for text mode? What location/offset data is the fpos_t object filled with if not number of bytes?

我了解 C 中 ftell() 和 fseek() 的工作原理,但是对于这个问题,我无法在任何地方找到任何精确的答案,包括 Whosebug 上最近的 post(LINK).

所以请您回答以下问题:

The function fills the fpos_t object pointed by pos with the information needed from the stream's position indicator to restore the stream to its current position

fgetpos() and fsetpos() 与文本和二进制模式相关。

fgetpos()的优点是保留流中的完整位置,包括其内部状态,以便您稍后可以恢复。无论您是否处于文本模式,这都有效。如果您在同一文件中使用 wide oriented streams or mix fgetc() and fgetwc(),这一点尤其重要,因为某些语言环境使用状态相关的多字节编码(状态取决于之前的读取)。

fseek()ftell() 也可以使用文本和二进制模式。然而,在文本模式中有一个重要的限制:你应该只使用 fseek() 和 0 或之前由 ftell() 返回的值(在二进制模式中你可以使用任何你想要的值)。这是因为与文件中的有效字节相比,文本模式读取可以更改从读取返回的字节数(典型示例,windows 文件中的 2 个 CR+LF 字节被转换为 signe LF 字节).

由于 ftell() 只有 returns 一个 long int 偏移量,如果需要的话,它无法跟踪多字节状态。所以使用 fseek() 可能会失去这种状态。

不完全是。可以从Beej:

中找到线索

On virtually every system (and certainly every system that I know of), people don't use these functions, using ftell() and fseek() instead. These functions exist just in case your system can't remember file positions as a simple byte offset.

Linux man pages

On some non-UNIX systems, an fpos_t object may be a complex object and these routines may be the only way to portably reposition a text stream.

Windows 上:

It assumes that any \n character in the buffer was originally a \r\n sequence that had been normalized when the data was read into the buffer.

也就是说,不是(Windows-linebreak)文本文件的文件在以文本模式打开时在 Windows 中出错,因为 fsetpos 假设文件确实存在是一个(Windows-换行符)文本文件,因此不能包含没有 \r.

\n

C11 标准说(我强调):

7.21.2/6:

Each wide-oriented stream has an associated mbstate_t object that stores the current parse state of the stream. A successful call to fgetpos stores a representation of the value of this mbstate_t object as part of the value of the fpos_t object. A later successful call to fsetpos using the same stored fpos_t value restores the value of the associated mbstate_t object as well as the position within the controlled stream.

请注意 fseekftellmbstate_t 对象无话可说:他们不报告或恢复它。因此,在面向广泛的流(也就是说,您使用面向广泛的 I/O 函数的流)上,它们只会重置文件位置,而不是(如果实现实际上有多个可能的 a 值) mbstate_t object) 流的整个状态。

面向宽的流与文本流不是一回事,只是读取宽文本文件是它们的常见用途。实际上 fseekftell 被记录为能够重置文本文件上的文件位置,前提是您正确使用它们。所以我相信(我可能是错的)只有在流上使用宽 I/O 函数时才需要 fsetposfgetpos

除了其他答案中提到的原因外,如果您正在处理非常大的文件,则可能需要使用fgetposfsetpos,包含超过 LONG_MAX 字节的文件。对于 LONG_MAX 为 231 − 1 的系统,这是一个真正的问题;如今,超过 20 亿字节的文件并不少见。

如果您使用的系统实现了 POSIX.1-2001,则有更好的选择,即在包含任何系统头文件之前 #define _FILE_OFFSET_BITS 64,然后使用 fseeko and ftello.这些就像 fseekftell 除了他们 take/return 一个 off_t 数量,如果你已经做了上面的 #define,保证是一个可以表示 263 − 1 的整数类型,这对任何人来说都应该足够了。这样更好,因为您可以对 off_t 进行算术运算;你不能使用 fpos_t 去你没去过的地方。但是,如果您不在 POSIX 系统上,fgetposfsetpos 可能是您唯一的选择。

(请注意,某些系统会给您一个 fpos_t 不能 表示大于 LONG_MAX 字节的文件偏移量。在其中一些,应用相同的 #define _FILE_OFFSET_BITS 64 设置会有所帮助。在其他情况下,如果您想要一个大文件,您就完全不走运了。)