什么时候`ifstream :: readsome`设置`eofbit`?

时间:2012-02-08 10:52:20

标签: c++ iostream

此代码永远循环:

#include <iostream>
#include <fstream>
#include <sstream>

int main(int argc, char *argv[])
{
    std::ifstream f(argv[1]);
    std::ostringstream ostr;

    while(f && !f.eof())
    {
        char b[5000];
        std::size_t read = f.readsome(b, sizeof b);
        std::cerr << "Read: " << read << " bytes" << std::endl;
        ostr.write(b, read);
    }
}

这是因为readsome永远不会设置eofbit

cplusplus.com说:

  

通过修改内部状态标志来发出错误信号:

     

eofbit get指针位于流缓冲区内部输入的末尾   调用函数时的数组,意味着没有位置   读入内部缓冲区(可能是或不是输入的结尾)   序列)。当rdbuf()->in_avail()-1之前返回failbit时会发生这种情况   提取第一个字符。

     

badbit该流位于之前的字符源的末尾   功能被称为。

     

[C++11: 27.7.2.3]:发生了上述错误。

几乎相同,标准说:

  

streamsize readsome(char_type* s, streamsize n); !good()

     

32。效果:表现为无格式输入功能(如中所述)   27.7.2.3,第1段)。构建岗哨对象后,如果setstate(failbit)呼叫   s可能会抛出异常并返回。否则提取   字符并将它们存储到第一个数组的连续位置   元素由rdbuf()->in_avail() == -1指定。如果setstate(eofbit),请致电   ios_base::failure(可能会抛出rdbuf()->in_avail() == 0(27.5.5.4))和摘录   没有人物;

     
      
  • 如果rdbuf()->in_avail() > 0,则不提取任何字符
  •   
  • 如果min(rdbuf()->in_avail(),n)),则提取in_avail() == 0
  •   
     

33。返回:提取的字符数。

如果流缓冲区为空,ifstream::readsome条件为无操作意味着in_avail() == -1本身是无操作,但eofbit条件意味着它某些其他操作导致in_avail() == -1会设置readsome

这似乎是一种不一致,即使readsome具有“某种”性质。

那么 eof和{{1}}的语义是什么?我是否正确地解释了它们?它们是流库中设计不良的一个例子吗?


(从[IMO]无效libstdc++ bug 52169被盗。)

4 个答案:

答案 0 :(得分:4)

我认为这是一个自定义点,默认流实现并未真正使用。

in_avail()返回它在内部缓冲区中可以看到的字符数(如果有)。否则,它会调用showmanyc()来尝试检测是否已知其他字符可用,因此保证缓冲区填充请求成功。

反过来,showmanyc()将返回它知道的字符数(如果有的话),如果知道读取将失败,则返回-1,如果不是,则返回0有一点线索。

默认实现(basic_streambuf)始终返回0,因此除非您有一个其他streambuf覆盖showmanyc的流,否则这就是您所获得的。

你的循环本质上就像你所知道的那样很多,而且当它为零时就会被卡住(意思是“#34;不确定&#34;”)。

答案 1 :(得分:1)

如果没有可用字符(即gptr() == egptr()的{​​{1}}),则调用虚拟成员函数std:streambuf。我可以使用showhowmanyc()的实现来返回错误代码。为什么这可能有用是一个不同的问题。但是,这可以设置showmanyc()。当然,eof()意味着不会失败,也不会阻止并返回已知可用的字符。也就是说,除非你有一个相当奇怪的流缓冲区,否则上面的循环基本上保证是一个无限循环。

答案 2 :(得分:1)

我认为readome()不适用于你要做的事情(从磁盘上的文件中读取)...来自cplusplus.com:

  

该函数旨在用于从某些数据中读取二进制数据   可能等待更多字符的异步源类型,因为   当局部缓冲器耗尽时停止读取,避免潜在的   意外的延误。

所以听起来像readsome()适用于来自网络套接字或类似内容的流,你可能只想使用read()。

答案 3 :(得分:1)

其他人已回答为什么readsome未能按设计设置eofbit。我将建议一种方法来读取一些字节,直到eof,而不是以直观的方式设置fail位,就像尝试使用readsome一样。这是另一个question研究的结果。

#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;

streamsize Read(istream &stream, char *buffer, streamsize count)
{
    // This consistently fails on gcc (linux) 4.8.1 with failbit set on read
    // failure. This apparently never fails on VS2010 and VS2013 (Windows 7)
    streamsize reads = stream.rdbuf()->sgetn(buffer, count);

    // This rarely sets failbit on VS2010 and VS2013 (Windows 7) on read
    // failure of the previous sgetn()
    stream.rdstate();

    // On gcc (linux) 4.8.1 and VS2010/VS2013 (Windows 7) this consistently
    // sets eofbit when stream is EOF for the conseguences  of sgetn(). It
    // should also throw if exceptions are set, or return on the contrary,
    // and previous rdstate() restored a failbit on Windows. On Windows most
    // of the times it sets eofbit even on real read failure
    stream.peek();

    return reads;
}

int main(int argc, char *argv[])
{
    ifstream instream("filepath", ios_base::in | ios_base::binary);
    while (!instream.eof())
    {
        char buffer[0x4000];
        size_t read = Read(instream, buffer, sizeof(buffer));
        // Do something with buffer 
    }
}