【问题标题】:Why does `fseek(..., 0, SEEK_CUR)` fail on windows?为什么 `fseek(..., 0, SEEK_CUR)` 在 Windows 上会失败?
【发布时间】:2018-04-25 15:45:45
【问题描述】:

我已经测试了以下 C 代码

#include <stdio.h>

int main()
{
    FILE * file = fopen("ans.txt", "r+");
    printf("%ld", ftell(file));  // prints 0
    fgetc(file);
    printf("%ld", ftell(file));  // prints -18
    printf("%d", fseek(file, 0, SEEK_CUR)); // -1
    printf("%ld", ftell(file));  // prints 150
    fclose(file);
    return 0;
}

在 Win10 上使用 MinGW-W64 (gcc version 7.1.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project)) 和 Visual Studio 2017 (cl.exe 版本 Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25547)
ans.txt 文件是(行以 unix 样式结尾)

line 1
line 2
line 3
line 4
line 5
line 6
line 7
line 8
line 9
line 10
line 11
line 12
line 13
line 14
line 15
line 16
line 17
line 18
line 19
line 20

但在 Arch Linux 上或当我以二进制模式打开文件或将行尾样式更改为“Windows/Mac OS 9”时一切正常。
和windows crt有关系吗?

【问题讨论】:

  • 如果您在 Windows 上以非二进制模式打开文件,它应该具有 Windows 样式的行尾。
  • 你检查你打开文件成功了吗?如果失败,代码没有义务崩溃。在数字之后打印换行符(或空格)也是明智的。
  • fgets()的意义是什么?你的代码没有调用它?或者,更准确地说,您当前要求我们审查的代码不会调用fgets()。据推测,这意味着您向我们展示的内容与您正在测试的代码不同,并且差异包含部分问题。我们通常无法推断出我们看不到的代码有什么问题。
  • 它在别处测试过。我的意思是程序可以知道行尾在哪里,但ftell 只是返回一个错误的位置,这似乎不合理。这不是主要问题,所以我没有提到它。很抱歉没有说清楚。
  • @BoPersson 谢谢你的想法!但由于非 Windows 行结束文件无法在文本模式下正确处理,难道不应该将其视为行没有结束吗?那么位置应该还是不会错的。

标签: c windows file stdio line-endings


【解决方案1】:

这记录在 MSDN here:

对于以文本模式打开的流,fseek 和 _fseeki64 的使用有限, 因为回车换行翻译会导致 fseek 和 _fseeki64 产生意想不到的结果。唯一能保证对以文本模式打开的流起作用的 fseek 和 _fseeki64 操作是:

  • 相对于任何原点值的偏移量为 0。

  • 当使用 fseekor _ftelli64 时,从文件的开头寻找一个偏移值,该偏移值是从对 ftell 的调用返回的 使用 _fseeki64。

以二进制模式打开文件...您会得到更可预测的结果:

FILE * file = fopen("ans.txt", "rb+");

【讨论】:

  • 是的。 fseek 之前的 fgetc 调用使流不正常。我没有时间通过​​ CRT 源进行调试以查看问题所在。鉴于原始源文件是 unix 样式的行结尾,OP 可以通过打开二进制而不是文本来避免所有这些翻译内容和相关的副作用。
  • 这不是 Windows 的一些奇怪的怪癖。 C 标准描述了对以文本模式打开的文件的 fseek 和 ftell 的相同限制。 ftell 的返回值本质上是一个不透明的魔法值,仅可用作 fseek 的输入。
  • @selbie,一般来说它与fgetc 无关。要获取当前位置,CRT 必须获取 OS 文件位置并从流缓冲区中减去未读字节。为此,它必须假设缓冲区中的 LF 是磁盘上的 CRLF,因为流缓冲区中的文本已经被翻译。对于具有 Unix 风格的 LF 行结尾的文件,该假设是错误的,我们最终会尝试寻找负文件位置,这会导致 SetFilePointerEx 失败。
  • @eryksun - 我可以建议你写下你自己的答案吗?我相信你有一个不同的,也许比我更好的观点。我也可能会赞成您的回答。
  • @selbie,你有唯一实用的解决方案——使用二进制模式。
猜你喜欢
  • 2021-03-16
  • 1970-01-01
  • 2020-09-18
  • 1970-01-01
  • 2013-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-03
相关资源
最近更新 更多