gpt4 book ai didi

c - ftell 如何影响以 'r' 而不是 'rb' 模式读取的二进制文件?

转载 作者:行者123 更新时间:2023-12-04 11:50:33 25 4
gpt4 key购买 nike

我有一个相当奇怪的问题,实际上根本不是很实用。错误(以 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 results

现在,如果我只是删除 ftell完全行,它给了我-

without ftell results

只有 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。

编辑:我也尝试添加 memcard == NULLfopen 之后立即检查,同样的行为。

编辑:通过@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;

}

2 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)
正如预期的那样,如果我更改 ftellfseek(memcard, 0, SEEK_CUR) , EOF被重置, reached after从不打印。

最佳答案

正如一些评论者指出的那样,它遇到了 EOF , 和 ftell实际上摆脱了那个EOF。为什么?要找到答案,我们必须查看 glibc 的源代码。我们可以找到 source for ftell : :

long int
_IO_ftell (FILE *fp)
{
off64_t pos;
CHECK_FILE (fp, -1L);
_IO_acquire_lock (fp);
pos = _IO_seekoff_unlocked (fp, 0, _IO_seek_cur, 0);
if (_IO_in_backup (fp) && pos != _IO_pos_BAD)
{
if (_IO_vtable_offset (fp) != 0 || fp->_mode <= 0)
pos -= fp->_IO_save_end - fp->_IO_save_base;
}
_IO_release_lock (fp);
if (pos == _IO_pos_BAD)
{
if (errno == 0)
__set_errno (EIO);
return -1L;
}
if ((off64_t) (long int) pos != pos)
{
__set_errno (EOVERFLOW);
return -1L;
}
return pos;
}
libc_hidden_def (_IO_ftell)

weak_alias (_IO_ftell, ftell)

这是重要的一行:

pos = _IO_seekoff_unlocked (fp, 0, _IO_seek_cur, 0);

让我们找到 source for _IO_seekoff_unlocked :

off64_t
_IO_seekoff_unlocked (FILE *fp, off64_t offset, int dir, int mode)
{
if (dir != _IO_seek_cur && dir != _IO_seek_set && dir != _IO_seek_end)
{
__set_errno (EINVAL);
return EOF;
}

/* If we have a backup buffer, get rid of it, since the __seekoff
callback may not know to do the right thing about it.
This may be over-kill, but it'll do for now. TODO */
if (mode != 0 && ((_IO_fwide (fp, 0) < 0 && _IO_have_backup (fp))
|| (_IO_fwide (fp, 0) > 0 && _IO_have_wbackup (fp))))
{
if (dir == _IO_seek_cur && _IO_in_backup (fp))
{
if (_IO_vtable_offset (fp) != 0 || fp->_mode <= 0)
offset -= fp->_IO_read_end - fp->_IO_read_ptr;
else
abort ();
}
if (_IO_fwide (fp, 0) < 0)
_IO_free_backup_area (fp);
else
_IO_free_wbackup_area (fp);
}

return _IO_SEEKOFF (fp, offset, dir, mode);
}

基本上,它只是做一些检查然后调用 _IO_SEEKOFF ,所以 let's find its source :

/* The 'seekoff' hook moves the stream position to a new position
relative to the start of the file (if DIR==0), the current position
(MODE==1), or the end of the file (MODE==2).
It matches the streambuf::seekoff virtual function.
It is also used for the ANSI fseek function. */
typedef off64_t (*_IO_seekoff_t) (FILE *FP, off64_t OFF, int DIR,
int MODE);
#define _IO_SEEKOFF(FP, OFF, DIR, MODE) JUMP3 (__seekoff, FP, OFF, DIR, MODE)

所以基本上, ftell最终调用了一个等价于 fseek(fp, 0, SEEK_CUR) 的函数.而在 fseek我们看到的标准:“成功调用 fseek() 函数会清除流的文件结束指示符。”这就是为什么 ftell改变程序的行为。

关于c - ftell 如何影响以 'r' 而不是 'rb' 模式读取的二进制文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62302014/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com