【问题标题】:fseek() on text files in C languagec语言文本文件上的fseek()
【发布时间】:2021-12-07 19:09:49
【问题描述】:

我开始学习 C 并且我正在使用文本文件。从它们加载文本,使用它,然后在同一个文件中更新它。我被告知 fseek() 不能保证每次都在文本文件中工作,我无法真正理解为什么。如果有人能解释一下,那就太好了!!

我还发现如果你这样做了

pos = ftell(file);
fseek(pos);

保证将指针移动到“pos”。是这样吗?

【问题讨论】:

  • 您应该阅读库函数 fseek 和 ftell 的文档。此外,您应该包含真实代码(这不是您调用 fseek 的方式)。
  • Ignacio Romero,fseek() 适用于文件的开头、结尾和ftell() 的前一个位置。它不适用于这 3 个位置的计算偏移量。有关更多详细信息,最好查看 C 规范。
  • @IgnacioRomero:您可以通过单击分数下方的灰色复选标记来接受其中一个答案。
  • @chux-ReinstateMonica 要了解更多细节,最好查看 C 规范 Indeed: "将文件位置指示器设置为文件结尾,与 @987654327 一样@,具有二进制流的未定义行为 ...”和then:“ftell 函数获取文件位置指示符的当前值...对于文本流.. . 两个这样的返回值之间的差异不一定是衡量写入或读取字符数的有意义指标"
  • (cont) 我不知道为什么 fseek()/ftell() 被广泛教授作为获取文件大小的一种方法。它之所以有效,是因为实现超出了 C 标准。例如,POSIX 说来自ftell() 的返回是一个字节偏移量。 Windows 也适用于二进制流,但不适用于文本流。 fstat(fileno(fp), &sb) 然后读 sb.st_size 可能是 more portable 而不是 fseek()/ftell()

标签: c file fseek file-pointer


【解决方案1】:

有人告诉我 fseek() 不能保证每次在文本文件中都能正常工作。

这是真的,但需要解释一下:

一些遗留系统使用多个字节对文本文件中的行尾进行编码,或者使用其他一些方案(例如固定长度记录)...这使得文件偏移量与从流中读取的字节数不同。实际上,有些文件偏移量在文本文件中是没有意义的,比如LF字节在CR/LF序列中的偏移量。此功能在写入文本文件时也是一个问题,在更新模式下使用相同的流指针读取和写入同一文件时更是如此。

在文本文件和二进制文件只是字节序列和由单个换行字节表示的行结尾的 Unix 系统上,这从来不是问题。

在将 C 语言移植到其他操作系统时,编译器供应商想出了各种巧妙的技巧来处理将系统特定的行结尾转换为单个 '\n' 字节。

由于这些技巧是针对特定系统甚至特定于供应商的,因此在 1989 年 ANSI 起草第一个 C 标准时,没有标准方法可以标准化。他们刚刚就fopen() 模式参数的b 标志达成一致,并消除了对ftell() 的返回值含义的任何限制,超出了简单的限制:

C19 7.21.9.2 fseek 函数

概要

#include <stdio.h>
int fseek(FILE *stream, long int offset, int whence);

说明

fseek 函数为stream 指向的流设置文件位置指示符。如果发生读取或写入错误,则会设置流的错误指示符并且fseek 失败。

对于二进制流,新位置(以文件开头的字符为单位)通过将offset 添加到whence 指定的位置获得。如果whenceSEEK_SET,则指定位置是文件的开头,如果SEEK_CUR,则为文件位置指示符的当前值,如果SEEK_END,则为文件结尾。二进制流不需要有意义地支持whence 值为SEEK_ENDfseek 调用。

对于文本流,offset 应为零,或 offset 应为先前成功调用与同一文件关联的流上的 ftell 函数返回的值,whence 应为SEEK_SET.

请注意,您的问题中的 fseek(pos); 不正确,因为缺少 stream 和 wherece 参数。你应该写:

long pos = ftell(file);
...
fseek(file, pos, SEEK_SET);  // move back to position <pos>

但请注意,fseek() 可能因其他原因而失败:并非所有流都支持查找,例如管道、终端连接和其他字符设备...文件偏移量可能超出类型 long 的范围,尤其是在遗留系统上, fgetpos() / fsetpos() 是首选替代方案(如果可用)。

【讨论】:

  • 非常感谢!现在我知道当时发生了什么。我会尝试按预期使用 fseek
  • “他们刚刚就 fopen() 模式参数的 t 和 b 标志达成一致” --> 我在 C 规范中没有看到 't' 标志。也许这是一些实现扩展。
  • @chux-ReinstateMonica:说得好。 t 标志只是另一个 Microsoft 扩展,用于在默认翻译模式通过 _set_fmode() 或全局变量 _fmode 设置为二进制时为新流选择文本模式处理(参见 docs.microsoft.com/en-us/cpp/c-runtime-library/… ...)令人惊讶的是,在过去的 40 年里,为了让最初的 CP/M 公约保持活力,浪费了多少能源。 RIP Gary Kildall.
  • @chqrlie 自 1870 年代以来,我们一直使用 QWERTY。一些设计选择继续困扰
【解决方案2】:

Ffseek() 用于将与给定文件关联的文件指针移动到特定位置。语法:fseek(FILE *pointer, long int offset, int position),offset:从位置偏移的字节数,位置:添加偏移量的位置。 position 定义文件指针需要移动的点。它具有三个值: SEEK_END :表示文件结束。 SEEK_SET :它表示文件的开始。 SEEK_CUR :它表示文件指针的当前位置。 // 将指针移动到结尾 我们需要指定位置 fseek(fp, 0, SEEK_END); // 打印指针位置

    printf("%ld", ftell(fp)); 你返回的东西也是写的,但是指定偏移位置要好得多

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多