【发布时间】:2020-09-29 18:37:59
【问题描述】:
我有一个相当奇怪的问题,实际上一点也不实用。错误(在r 模式下读取二进制文件)显而易见,但我被别的东西弄糊涂了。
这是代码-
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdint.h>
#define BUFFER_LEN 512
typedef uint8_t BYTE;
int main()
{
FILE* memcard = fopen("card.raw", "r");
BYTE buffer[BUFFER_LEN];
int count = 0;
while (fread(buffer, sizeof(*buffer), BUFFER_LEN, memcard) != 0)
{
printf("count: %d\n", count++);
}
fclose(memcard);
return 0;
}
现在,card.raw 是一个二进制文件,因此由于在 r 模式而不是 rb 中读取,此读取将出错。但我很好奇的是,那个循环恰好执行了 3 次,在最后的执行中,它甚至没有读取 512 个字节。
现在如果我将该循环更改为
while (fread(buffer, sizeof(*buffer), BUFFER_LEN, memcard) != 0)
{
printf("ftell: %ld\n", ftell(memcard));
}
它不再在 3 次处决时停止。事实上,它一直持续到(大概)文件结束。 fread 计数仍然混乱。许多读取不会在读取元素时返回 512。但这很可能是由于文件以r 模式打开以及伴随的所有编码错误。
ftell 不应该影响文件本身,那么为什么在循环中包含ftell 会使它执行更多次?
我决定对循环进行更多更改以提取更多信息-
while ((count = fread(buffer, sizeof(*buffer), BUFFER_LEN, memcard)) != 0)
{
printf("fread bytes read: %d\n", count);
printf("ftell: %ld\n", ftell(memcard));
}
如果ftell 包含在循环中并且前几个结果看起来像这样,则此循环的次数与它一样多-
现在,如果我完全删除 ftell 行,它会给我-
只有 3 次处决,但没有任何改变。
这种行为背后的解释是什么?
注意:我知道 fread 和 ftell 返回的计数可能由于读取模式而出错,但这不是我关心的问题。我只是好奇 - 为什么有区别,包括 ftell 和不包括它。
另外,如果有帮助,card.raw 文件实际上只是 cs50 pset4“存储卡”。您可以通过wget https://cdn.cs50.net/2019/fall/psets/4/recover/recover.zip 获取它并将输出文件存储在.zip
编辑:我应该提到这是在 Windows 上使用 VS2019 的 clang 工具。命令行选项(从 VS2019 项目属性中检查)看起来像-
/permissive- /GS /W3 "Debug\" "Debug\" /Zi /Od "Debug\vc142.pdb" /fp:precise /D "_CRT_SECURE_NO_WARNINGS" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /WX- /Gd /MDd /Fa"Debug\" /EHsc /nologo /Fo"Debug\" /Fp"Debug\Test.pch" /diagnostics:column
编辑:另外,我确实检查了循环内的ferror,无论有无ftell,都没有得到任何错误。事实上,feof 在循环之后返回 1,在这两种情况下。
编辑:我还尝试在 fopen 之后添加一个 memcard == NULL 检查,同样的行为。
编辑:解决@orlp 的答案。事实上,我确实检查了错误。不过我肯定应该发布它。
while ((count = fread(buffer, sizeof(*buffer), BUFFER_LEN, memcard)) != 0)
{
if ((err = ferror(memcard)))
{
fprintf(stderr, "Error code: %d", err);
perror("Error: ");
return 1;
}
printf("fread bytes read: %d\n", count);
printf("ftell: %ld\n", ftell(memcard));
}
if ((err = ferror(memcard)))
{
fprintf(stderr, "Error code: %d", err);
perror("Error: ");
return 1;
}
两个if 语句都没有被触发。
编辑:我以为我们已经得到了答案,它是 ftell 重置 EOF。但是我把循环改成了-
while ((count = fread(buffer, sizeof(*buffer), BUFFER_LEN, memcard)) != 0)
{
if ((err = ferror(memcard)))
{
fclose(memcard);
fprintf(stderr, "Error code: %d", err);
perror("Error: ");
return 1;
}
if (feof(memcard))
{
printf("reached before\n");
}
printf("fread bytes read: %d\n", count);
ftell(memcard);
if (feof(memcard))
{
printf("reached after\n");
}
}
这会触发第一个if(feof) 和第二个if(feof)
不过,正如预期的那样,如果我将 ftell 更改为 fseek(memcard, 0, SEEK_CUR),EOF 会重置,而 reached after 永远不会打印出来。
【问题讨论】:
-
> 现在,
card.raw是一个二进制文件,因此由于在r模式而不是rb模式下读取,此读取会出错。不,它不一定会出错。这取决于您的平台。 -
@chux-ReinstateMonica 抱歉,我不知道它是如何出现在问题中的。我在测试中没有使用
&,不用担心。 -
"在最终执行中,它甚至没有读取 512 个字节。"和“fread 计数仍然搞砸了。”让我担心的是代码
while (fread(buffer, sizeof(*buffer), BUFFER_LEN, memcard) != 0)不会更新count,因此这些结论基于发布的代码以外的其他内容。我建议发布每个步骤使用的确切代码以改进调查。 -
如果没有
ftell,它会在遇到0x1a字符时停止,这意味着Windows 文本模式文件中的EOF。我不知道为什么打电话给ftell会改变这一点。 -
@interjay 嗯,非常有用的评论。