FSEEK offset 接受的内容超过了它应该接受的内容

FSEEK offset accepts more than what it should accept

关注 Specification:

For a text stream, either offset shall be zero, or offset shall be a value returned by an earlier successful call to the ftell function on a stream associated with the same file and whence shall be SEEK_SET.

我理解 offset 必须是 ftell 函数的返回值,或者 0,并且 whence 必须是 SEET_SET(或 0)。但是我使用了一些整数作为偏移量和不同的SEEK_...,它似乎工作得很好。

例如,这些有效:

fseek(file, 4, SEEK_CUR);
fseek(file, -1, SEEK_END);
fseek(file, 0, SEEK_CUR);

当我阅读规范时,我觉得它应该不起作用。我多次尝试以这种方式使用 fseek,但从未失败。为什么它有效,我没有得到什么?

在 ftell 文档中您可以阅读

For text streams, the numerical value may not be meaningful but can still be used to restore the position to the same position later using fseek (if there are characters put back using ungetc still pending of being read, the behavior is undefined).

您引用的内容意味着,如果您知道要将指针放置在何处,那么使用它可能是有意义的,并且您可能知道它是因为您优先调用了 ftell()。

你对 fseek 的所有调用都是有效的,但在文本文件中使用 fseek 移动没有多大意义,因为它不是随机访问(二进制)文件,但这并不意味着它是错误的使用它。

对于文本文件,您可以找到 here 访问它的最常用函数,例如 fscanf()、fprintf() 等。

When I read the specification it seems to me that it should not work.

规范说明了必须工作的内容。它应该被视为创建 c 库的人的最低要求(即 fseek 等人的实现者)。

不正确的使用可能仍然有效,但不能保证。结果将取决于平台。

例如,fseek 的 Linux 手册页说:

The fseek() function sets the file position indicator for the stream pointed to by stream. The new position, measured in bytes, is obtained by adding offset bytes to the position specified by whence. If whence is set to SEEK_SET, SEEK_CUR, or SEEK_END, the offset is relative to the start of the file, the current position indicator, or end-of-file, respectively. A successful call to the fseek() function clears the end-of-file indicator for the stream and undoes any effects of the ungetc(3) function on the same stream.

您可以看到,您尝试过的操作在 Linux 中对文本流和二进制流都有效。但是,可能存在 fseek 无法与 SEEK_CUR 或 SEEK_END 一起用于文本流的平台。

另请注意,流可以与不同的事物相关联:文件、键盘、套接字、终端 window、设备等。

您的所有 fseek 通话均有效。您作为第二个参数提供的数字是一个 offset,这意味着它与您作为第三个参数提供的搜索类型有关。

fseek(file, 4, SEEK_CUR);    // seek 4 bytes forward from current position
fseek(file, -1, SEEK_END);   // seek to 1 byte before the end of the file
fseek(file, 0, SEEK_CUR);    // does nothing.

但另请参阅用户 Tu.ma 的解释,即搜索位置不准确 and/or 如果文件以文本模式打开(尤其是在 Windows 下,则可能毫无意义,因为运输 return/line 提要翻译)。

没有什么能阻止您使用 fseek 超越文件的当前大小。因为这样做允许您在该点写入数据,用 NUL 填补尚未写入的空白。与此示例代码一样 - 它创建一个包含 1000 个 NUL 的文件,然后 "hello\n"

#include <stdio.h>

int main(void)
    {
    FILE *f;

    f=fopen("test","w"); 
    if(f)
        {
        fseek(f,1000,SEEK_SET);
        fprintf(f,"hello\n");
        fclose(f);
        }
    else
        {
        perror("fopen");
        }
    }

我认为 fseek 的定义与 C 标准中的定义相同的主要原因是您在文本文件中的逻辑位置可能与文件开头的物理字节数无关文本文件。

例如,在 Windows 实现中,将磁盘文件中的 \r\n 转换为 \n 以保持与 Unix 行尾的兼容性并不少见。因此,如果您的文件如下所示:

hello\r\nworld

即两行,你 fseek 到位置 6,你希望在 \n 还是 w 上?如果您试图通过在 Windows 上使用 fgetc 来计算字符数,您会假设您会在 w 上。但是 fseek 可能会在不扫描行尾的情况下前进到字节 6。

编辑

And if we use the fgetc function, each character that we read increases our position of 1: the file cursor goes to the next character after the previous one was read. Is that a problem?

是的。问题出在 "character" 的定义中。如果您处于使用 DOS 约定的环境中,当接下来的两个字节为 0x0d 0x0a 时,在文本流上使用 fgetc 会使文件位置前进两个,但仅 returns 0x0a.实施可能会选择进行其他转换,例如将分解的 Unicode 转换为预组合的 Unicode,反之亦然。

C 标准中的措辞允许实现丢失文件中的字节与 fgetc 返回的字符之间的一对一映射,而不必过于复杂 fseek