如何检测二进制文件已被完全消耗

How Can I Detect That a Binary File Has Been Completely Consumed?

本文关键字:二进制文件 何检测 检测      更新时间:2023-10-16

如果我这样做:

ofstream ouput("foo.txt");
output << 13;
output.close();
ifstream input("foo.txt");
int dummy;
input >> dummy;
cout << input.good() << endl;

我会得到结果:"0"

然而,如果我这样做:

ofstream ouput("foo.txt", ios_base::binary);
auto dummy = 13;
output.write(reinterpret_cast<const char*>(&dummy), sizeof(dummy));
output.close();
ifstream input("foo.txt", ios_base::binary);
input.read(reinterpret_cast<char*>(&dummy), sizeof(dummy));
cout << input.good() << endl;

我会得到结果:"1"

这让我很沮丧。我是否必须通过检查ifstream的缓冲区来确定它是否已被完全消耗?

关于

如何检测二进制文件已被完全消耗?

一种效率稍低但易于理解的方法是测量文件的大小:

ifstream input("foo.txt", ios_base::binary);
input.seekg(0, ios_base::end); // go to end of the file
auto filesize = input.tellg(); // current position is the size of the file
input.seekg(0, ios_base::beg); // go back to the beginning of the file

然后随时检查当前位置:

if (input.tellg() == filesize)
    cout << "The file was consumed";
else
    cout << "Some stuff left in the file";

这种方式有一些缺点:

  • 效率不高-在文件中来回移动
  • 不适用于特殊文件(例如管道)
  • 如果文件被更改,则不起作用(例如,您以读写模式打开文件)
  • 只适用于二进制文件(似乎是您的情况,所以可以),不适用于文本文件

因此,最好使用人们的常规方式,即尝试阅读并在失败时保释:

if (input.read(reinterpret_cast<char*>(&dummy), sizeof(dummy)))
    cout << "I have read the stuff, will work on it now";
else
    cout << "No stuff in file";

或者(在循环中)

while (input.read(reinterpret_cast<char*>(&dummy), sizeof(dummy)))
{
    cout << "Working on your stuff now...";
}

您正在做完全不同的事情。

CCD_ 2是贪婪的并且将尽可能多地读取到CCD_ 3中。碰巧在这样做的时候,它会跑到文件的末尾。这设置了input.eof(),并且流不再是good()。由于在手术结束前确实找到了一些数字,手术仍然成功。

在第二次读取中,您要求指定字节数(很可能是4),读取成功。所以流仍然是good()

流接口不会预测任何未来I/O的结果,因为在一般情况下它无法知道。如果您使用cin而不是input,那么如果用户继续键入,现在可能会有更多内容可供阅读。

具体来说,eof()状态直到有人试图读取文件末尾之后才会出现。

对于文本流,由于您只写入了整数值,甚至没有写入一个空格而不是行的末尾,因此在读取时,库必须尝试读取一个通过13并到达文件末尾的字符。所以好比特是假的,eof是真的。

对于二进制流,假设int是32位大,则写入4个字节(sizeof(int)),读取4个字节。好的仍然没有出现任何问题,好的部分是真的,而eof是假的。只有下一次读取才会到达文件末尾。

但要小心。在文本示例中,如果您在编辑器中打开文本文件并简单地保存它而不更改任何内容,那么编辑器很可能会自动添加一个行尾。在这种情况下,读取将在行尾停止,对于二进制情况,好比特将为true,eof为false。和你用output << 13 << std::endl; 写的一样

所有这一切都意味着,当good-it-true和eof为false时,您永远不能假设读取不是文件的最后一个元素,因为即使当时没有返回任何内容,文件的末尾也可能只在下一次读取时命中。

TL/DR:知道文件中什么都没有的唯一万无一失的方法是当你不能再从中读取一些东西时。

您不需要检查缓冲区。您可以确定整个文件是否已被消耗:cout << (input.peek() != char_traits<char>::eof()) << endl这使用:peek,它是:

从输入流中读取下一个字符,而不提取

示例中的good为:

  • 在最后一次提取操作之后返回false,这是因为int提取运算符必须读取,直到找到非数字的字符。在这种情况下,这是EOF字符,当该字符被读取时,即使作为分隔符,也会设置流的eofbit,导致good失败
  • 在调用operator>>1后返回true,因为read精确地提取sizeof(int)字节,所以即使EOF字符是下一个字符,也不会读取它,使流的eofbit未设置,good通过

CCD_ 26可以在这两种情况中的任何一种之后使用,并且在两种情况下都将正确返回CCD_ 27。实际上,这是为您检查缓冲区,但二进制文件有一个重要区别:如果您自己检查二进制文件,您会发现它可能包含EOF字符。(在大多数定义为0xFF的系统上,其中4个是-1的二进制表示。)如果您正在检查缓冲区的下一个字符,您将不知道这是否真的是文件的末尾。

然而,peek不仅仅返回char,它还返回dummy0。如果peek返回0x000000FF,那么您看到的是EOF字符,但不是文件的末尾。如果peek返回char_traits<char>::eof()(通常为0xFFFFFFFF),那么您看到的是文件的末尾。