为什么 EOF 会提前到达 EOF?

Why is fread reaching the EOF early?

提问人:James Schek 提问时间:10/3/2008 最后编辑:Jim FellJames Schek 更新时间:8/10/2015 访问量:16151

问:

我正在编写一个将文件读入内存的 C 库。它跳过文件的前 54 个字节(标头),然后将其余部分作为数据读取。我使用 fseek 来确定文件的长度,然后使用 fread 读取文件。

循环运行一次,然后结束,因为已达到 EOF(无错误)。最后,bytesRead = 10624,ftell(stream) = 28726,缓冲区包含 28726 个值。我预计 fread 将读取 30,000 字节,当达到 EOF 时,文件位置为 30054。

C语言不是我的母语,所以我怀疑我在某个地方犯了一个愚蠢的初学者错误。

代码如下:

const size_t headerLen = 54;

FILE * stream;
errno_t ferrno = fopen_s( &stream, filename.c_str(), "r" );
if(ferrno!=0) {
  return -1;
}

fseek( stream, 0L, SEEK_END );
size_t bytesTotal = (size_t)(ftell( stream )) - headerLen; //number of data bytes to read
size_t bytesRead = 0;
BYTE* localBuffer = new BYTE[bytesTotal];
fseek(stream,headerLen,SEEK_SET);
while(!feof(stream) && !ferror(stream)) {
    size_t result = fread(localBuffer+bytesRead,sizeof(BYTE),bytesTotal-bytesRead,stream);
    bytesRead+=result;
}

根据您使用的引用,很明显,在模式标志中添加“b”是答案。寻求骨头徽章的提名。:-)

这个参考资料在第二段第二句话中谈到了它(尽管没有在他们的表格中)。

MSDN 直到页面的一半才讨论二进制标志。

OpenGroup 提到了“b”标签的存在,但指出它“无效”。

C++ C 文件-IO stdio feof

评论

0赞 user7116 10/3/2008
我添加了 microsoft 标签fopen_s因为该标签仅在 MS 的 C RTL 中。
0赞 Jonathan Leffler 10/3/2008
OpenGroup 确实提到了它:r 或 rb - 打开文件进行阅读。...字符“b”无效,但允许符合 ISO C 标准。请修正您的编辑。
0赞 EvilTeach 12/13/2010
找到文件大小并分配内存后,您应该能够一口气读取整个文件。

答:

56赞 Evan Teran 10/3/2008 #1

也许这是一个二进制模式问题。尝试以模式打开文件。"r+b"

编辑:如评论中所述,它可能更符合您的原始意图,因为将打开它进行读/写并且是只读的。"rb""r+b""rb"

评论

0赞 user7116 10/3/2008
+1 代表 Mike F 和我自己。Windows 总是用 +b 咬我。
0赞 Greg Hewgill 10/3/2008
我建议先尝试“rb”,因为“r+b”打开文件进行读写,如果您不打算写入该文件,则应继续以只读方式打开它。
0赞 James Schek 10/3/2008
这就是答案。“RB”有效。RTFM的另一个案例。第一段也这样说:cplusplus.com/reference/clibrary/cstdio/fopen.html
0赞 Paul Tomblin 10/3/2008
我同意 Evan 的观点,这可能是一个二进制模式问题。但是,我很确定 C 标准并不能保证您正在使用的 ftell 将返回文件的实际长度。我似乎记得需要返回一个“令牌”,如果您将其传递给 fseek,它将让您回到文件中的相同位置。
0赞 James Schek 10/3/2008
我也有同样的想法......根据这个参考,它是实际位置:opengroup.org/onlinepubs/009695399/functions/ftell.html
0赞 Richard Harrison 10/3/2008 #2

还值得注意的是,只需将binmode.obj包含在链接命令中即可在所有文件打开时为您执行此操作。

-1赞 Kumar Pushkar 6/26/2014 #3

基于前面答案的解决方案:

    size_t bytesRead = 0;
    BYTE* localBuffer = new BYTE[bytesTotal];
    fseek(stream,headerLen,SEEK_SET);
        while(!feof(stream) && !ferror(stream)) {
        size_t result = fread(localBuffer+bytesRead,sizeof(BYTE),bytesTotal-
        bytesRead,stream);
    bytesRead+=result;
}