【问题标题】:FSEEK offset accepts more than what it should acceptFSEEK 偏移量接受的比它应该接受的多
【发布时间】:2017-05-09 12:27:07
【问题描述】:

关注Specification:

对于文本流,偏移量应为零,或偏移量应为先前成功调用与同一文件关联的流上的 ftell 函数返回的值,并且应为 SEEK_SET。

我知道offset 必须是ftell 函数的retun 值或0,而whence 必须是SEET_SET(或0)。但是我使用了一些整数作为偏移量和不同的SEEK_...,它似乎工作得很好。

例如,这些工作:

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

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

【问题讨论】:

  • 请出示您的代码。
  • 文件是否打开以供读取和/或写入?
  • ftell 返回您可能想用作偏移量的当前位置,返回的当前位置是long int。所以你可以使用任何长整数作为偏移量
  • Chris Turner 它以 r+ 模式打开。
  • Badda 我应该显示什么代码?我的问题是关于 fseek 函数以及它的一般工作原理

标签: c fseek


【解决方案1】:

在 ftell 文档中您可以阅读

对于文本流,数值可能没有意义,但可以 仍用于稍后使用将位置恢复到相同位置 fseek (如果有使用 ungetc 放回的字符仍然未决 正在读取,行为未定义)。

您所引用的意思是,如果您知道要将指针放在哪里,使用它可能是有意义的,并且您可能知道它,因为您优先调用了 ftell()。

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

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

【讨论】:

  • 在文本文件中什么比 fseek 更合适?
  • 考虑到您知道文本文件中的结构,您应该使用这些函数:LINK 无论如何,我将其添加到答案中。
  • 好吧,这些功能中是否有一个可以倒退,比如fseek(file, -1, SEEK_CUR)?在我看来,这些函数只读取下一个字符/行。而且我仍然不明白为什么使用fseek 不是最合适的
  • 如果你保存在一个文本文件“hello”和一个新行“world”上,你将如何使用 fseek 访问 'o'?如果你将它保存在 windows 中,它将被存储为“hello\r\nworld”,如果在 linux 中它只会有“hello\nworld”。这意味着如果您将文件作为文本打开,您要求对其进行解码和解释,并且使用 fseek() 的随机访问可能难以处理,使用 fscanf() 将一行读入字符串更有意义而不是在代码中计算它来访问你需要的东西。
  • 在 unix 上,像 UTF-8 这样的可变宽度编码会产生与在 Windows 上被诅咒的 \r\n 相同的问题。字节偏移量与字符数不同。如果你想要一个字节偏移量,只要避免在文本模式下打开就可以了!如果字符数是您想要的,那么您基本上必须放弃寻找,只需阅读您所在位置和要去的位置之间的所有字符。
【解决方案2】:

当我阅读规范时,我觉得它不应该起作用。

规范,说明什么必须起作用。它应该被视为创建 c 库的人(即 fseek 等的实现者)的最低要求。

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

例如,fseek的 Linux 手册页说:

fseek() 函数为 stream 指向的流设置文件位置指示符。新位置(以字节为单位)是通过将偏移字节添加到 wherece 指定的位置来获得的。如果 wherece 设置为 SEEK_SET、SEEK_CUR 或 SEEK_END,则偏移量分别相对于文件开头、当前位置指示符或文件结尾。成功调用 fseek() 函数会清除流的文件结束指示符并撤消 ungetc(3) 函数对同一流的任何影响。

如您所见,您尝试的方法在 Linux 中适用于文本流和二进制流。但是,可能存在fseek 无法与 SEEK_CUR 或 SEEK_END 一起用于文本流的平台。

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

【讨论】:

    【解决方案3】:

    您的所有fseek 呼叫均有效。您作为第二个参数提供的数字是一个偏移量,这意味着它与您作为第三个参数提供的搜索类型相关。

    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 的解释,即如果文件已以文本模式打开(尤其是在 Windows 下,由于回车/换行翻译),则查找位置不准确和/或可能毫无意义。

    【讨论】:

    • 严格来说,根据 C 规范,它们是无效的。我想这是因为,对于文本流,文件中的逻辑位置不一定与开头的字节数相同。
    【解决方案4】:

    没有什么可以阻止您使用 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");
            }
        }
    

    【讨论】:

      【解决方案5】:

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

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

      hello\r\nworld
      

      即两行,你 fseek 到第 6 位,你希望在 \nw 上吗?如果您尝试通过在 Windows 上使用 fgetc 来计算字符数来找出答案,您会假设您将使用 w。但是fseek 可能会在不扫描行尾的情况下前进到字节 6。

      编辑

      如果我们使用 fgetc 函数,我们读取的每个字符都会增加我们的位置 1:文件光标在读取前一个字符后转到下一个字符。有问题吗?

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

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

      【讨论】:

      • 据我了解,换行符是一个字符,第六个(从0开始计数)位置对应\n。如果我们使用fgetc 函数,我们读取的每个字符都会增加我们的位置 1:文件光标在读取前一个字符后转到下一个字符。那是问题吗?我知道编译器或某些库会在需要时自动将 /n 转换为 /r/n。
      • DOS/Windows 中的行尾历来由两个chars 一个0x0d 和一个0x0a 组成。 Windows C 库默默地在文本流中删除了 0x0d,使它们看起来像 Unix 文本流。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-12-13
      • 1970-01-01
      • 2019-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-26
      相关资源
      最近更新 更多