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

时间:2016-08-03 13:15:52

标签: c++ text-files binaryfiles ifstream is-empty

如果我这样做:

ofstream ouput("foo.txt");

output << 13;
output.close();

ifstream input("foo.txt");
int dummy;

input >> dummy;

cout << input.good() << endl;

我会得到结果:&#34; 0&#34;

但是,如果我这样做:

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;

我会得到结果:&#34; 1&#34;

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

4 个答案:

答案 0 :(得分:1)

关于

  

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

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

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...";
}

答案 1 :(得分:0)

你做的事情完全不同。

operator>>贪婪,会尽可能多地读到dummy。碰巧这样做,它会运行到文件的末尾。这会设置input.eof(),流不再是good()。因为它在结束前找到了一些数字,所以操作仍然成功。

在第二次读取中,您要求特定的字节数(最可能为4个)并且读取成功。所以流仍然是good()

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

具体来说,在有人试图读取文件结尾之前,eof()状态才会出现。

答案 2 :(得分:0)

对于文本流,因为您只写了整数值而不是空格而不是行尾,所以在读取时,库必须尝试读取通过1和{{1}的一个字符并命中文件的结尾。所以好的一点是假的,而且eof是真的。

对于二进制流,你写了4个字节(sizeof(int)),假设int是32位大,你读4个字节。精细。没有问题仍然存在,好的位是真的和错误的。只有下一次读取才会到达文件末尾。

但要注意。在文本示例中,如果您在编辑器中打开文本文件并只是保存它而不更改任何内容,则编辑器可能会自动添加行尾。在这种情况下,读取将在行尾停止,对于二进制情况,good位将为true且eof为false。你用3

写的也一样

所有这一切都意味着你必须永远不要假设读取​​不是文件的最后一个元素,如果它是好的并且eof是假的,因为即使没有返回任何内容,文件的结尾也可能只在下次读取时被命中。

TL / DR:知道文件中没有任何内容的唯一简单方法是当您无法再从中读取内容时。

答案 3 :(得分:0)

您无需借助检查缓冲区。您可以确定是否已使用整个文件:cout << (input.peek() != char_traits<char>::eof()) << endl这使用:peek,其中:

  

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

在示例的情况下,

good是:

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

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

peek不只是返回char,而是返回int_type。如果peek返回0x000000FF,那么您正在查看EOF字符,但文件末尾。如果peek返回char_traits<char>::eof()(通常为0xFFFFFFFF),那么您正在查看文件的末尾。