fseek() 在 C 语言的文本文件上

fseek() on text files in C language

提问人:Ignacio Romero 提问时间:10/21/2021 更新时间:10/24/2021 访问量:742

问:

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

我还发现,如果你这样做

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

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

C fseek 文件指针

评论

7赞 jarmod 10/21/2021
您应该阅读库函数 fseek 和 ftell 的文档。此外,你应该包含真正的代码(这不是你调用 fseek 的方式)。
0赞 chux - Reinstate Monica 10/21/2021
伊格纳西奥·罗梅罗(Ignacio Romero),为文件的开头、结尾和上一个地方工作。它不适用于这 3 个位置的计算偏移量。有关更多详细信息,最好查看 C 规范。fseek()ftell()
0赞 chqrlie 10/23/2021
@IgnacioRomero:您可以通过单击其分数下方的灰色复选标记来接受其中一个答案。
0赞 Andrew Henle 10/23/2021
@chux-ReinstateMonica 有关更多详细信息,最好查看 C 规范 Indeed:“将文件位置指示器设置为文件末尾,就像 一样,对二进制流具有未定义的行为...”,然后:“该函数获取文件位置指示器的当前值......对于文本流...两个此类返回值之间的差异不一定是写入或读取的字符数的有意义的度量fseek(file, 0, SEEK_END)ftell"
0赞 Andrew Henle 10/23/2021
(续)我不知道为什么/被如此广泛地教授为获取文件大小的一种方式。它之所以有效,是因为实现超出了 C 标准。例如,POSIX 表示返回 from 是一个字节偏移量。Windows 也适用于二进制流,但不适用于文本流。 然后阅读可能比 / 便携fseek()ftell()ftell()fstat(fileno(fp), &sb)sb.st_sizefseek()ftell()

答:

0赞 Anusree Tadikonda 10/22/2021 #1

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

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

1赞 chqrlie 10/22/2021 #2

我被告知,不能保证每次都能在文本文件中工作。fseek()

这是真的,但需要解释:

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

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

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

由于这些技巧是特定于系统甚至特定于供应商的,因此在 1989 年 ANSI 起草第一个 C 标准时,没有标准方法可以标准化。他们只是同意了 mode 参数的标志,并删除了对 beyond simple constraints 返回值含义的任何约束:bfopen()ftell()

C19 7.21.9.2 fseek 函数

概要

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

描述

该函数为 指向的流设置文件位置指示器。如果发生读写错误,则流的错误指示器设置并失败。fseekstreamfseek

对于二进制流,新位置(以文件开头的字符为单位)是通过与 指定的位置相加来获得的。指定的位置是文件的开头 if ,文件位置指示符的当前值 if ,或者文件结束 if 。二进制流不需要有意义地支持值为 的调用。offsetwhencewhenceSEEK_SETSEEK_CURSEEK_ENDfseekwhenceSEEK_END

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

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

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

但请注意,这可能由于其他原因而失败:并非所有流都支持搜索,例如管道、终端连接和其他字符设备......文件偏移量可能会超出类型范围,尤其是在旧系统上,如果可用,则是首选的替代方法。fseek()longfgetpos()fsetpos()

评论

0赞 Ignacio Romero 10/22/2021
非常感谢!现在我了解当时发生了什么。然后我会尝试按预期使用 fseek
0赞 chux - Reinstate Monica 10/24/2021
“他们刚刚就 fopen() 模式参数的 t 和 b 标志达成一致” --> 我在 C 规范中没有看到标志。也许这是一些实现扩展。't'
0赞 chqrlie 10/24/2021
@chux-ReinstateMonica:说得好。该标志只是另一个Microsoft扩展,用于在默认转换模式设置为二进制 via 或全局变量 ( cf learn.microsoft.com/en-us/cpp/c-runtime-library/... ...) 时为新流选择文本模式处理令人惊讶的是,在过去的 40 年里,为了保持最初的 CP/M 公约的活力,浪费了多少精力。RIP 加里·基尔德尔。t_set_fmode()_fmode
0赞 chux - Reinstate Monica 10/24/2021
@chqrlie 自 1870 年代以来,我们一直被 QWERTY 困住。一些设计选择仍然困扰着人们