【问题标题】:When does `ifstream::readsome` set `eofbit`?`ifstream::readsome` 何时设置 `eofbit`?
【发布时间】:2012-02-29 19:16:15
【问题描述】:

这段代码永远循环:

#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 获取指针位于流缓冲区内部输入的末尾 调用函数时的数组,这意味着没有位置可以 读取内部缓冲区(可能是也可能不是输入的结尾 顺序)。当rdbuf()-&gt;in_avail() 在 提取第一个字符。

failbit 流位于字符源的末尾 函数被调用。

badbit 发生了上述以外的错误。

几乎一样,标准说:

[C++11: 27.7.2.3]:streamsize readsome(char_type* s, streamsize n);

32.效果: 表现为一个未格式化的输入函数(如在 27.7.2.3,第 1 段)。构造哨兵对象后,如果!good()调用 setstate(failbit) 可能会抛出异常并返回。否则提取 字符并将它们存储到数组的连续位置中,其第一个 元素由s 指定。如果rdbuf()-&gt;in_avail() == -1,则调用 setstate(eofbit)(可能会抛出 ios_base::failure (27.5.5.4)),并提取 没有字符;

  • 如果rdbuf()-&gt;in_avail() == 0,则不提取字符
  • 如果rdbuf()-&gt;in_avail() &gt; 0,提取min(rdbuf()-&gt;in_avail(),n))

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

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

尽管readsome 具有“某种”性质,这似乎是不一致的。

那么readsomeeof 的语义是什么?我是否正确解释了它们?它们是流库中设计不佳的示例吗?


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

【问题讨论】:

    标签: c++ iostream


    【解决方案1】:

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

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

    反过来,showmanyc() 将返回它知道的字符数(如果有),或者如果它知道读取将失败,则返回 -1,如果没有线索。

    默认实现 (basic_streambuf) 总是返回 0,所以这就是你得到的结果,除非你有一个流覆盖了 showmanyc

    您的循环本质上是按您知道的安全方式读取的多字符数,当它为零时它会卡住(意思是“不确定”)。

    【讨论】:

      【解决方案2】:

      我不认为 readsome() 是为了你正在尝试做的事情(从磁盘上的文件中读取)...来自 cplusplus.com:

      该函数旨在用于从某些 可能等待更多字符的异步源类型,因为 当本地缓冲区耗尽时它会停止读取,避免潜在的 意外延误。

      所以听起来 readsome() 是用于来自网络套接字或类似东西的流,而您可能只想使用 read()。

      【讨论】:

      • 实际上我正在从 GPS 接收器读取 NMEA 流。但是 FWIW 原始代码不是我的;它来自另一个 SO question,我只是想合理化 .readsome 的行为;无论它是否适合文件,我都希望在达到 EOF 时设置eof
      • 啊,好的。如果代码的结构仍然反映上面的 sn-p,我仍然建议使用 read()... readsome() 似乎使用非阻塞语义,这只会在没有数据的情况下导致紧密的无操作循环进来。只是为了澄清一下:当套接字关闭时,这段代码仍然什么都不做?
      • 你说得对,这就是readsome()的行为。这个问题是关于这种行为的合理化,无论任何特定用途。而且我不确定当套接字关闭时会发生什么,因为我不再让原始测试用例在任何地方运行。
      • 我认为这是问题的根源:当 EOF 发生在流上时。如果没有可用数据,但将来可能有(管道、套接字或类似结构打开但当前没有可用数据),那么这与系统知道没有更多数据的情况不同(当管道/套接字/等已关闭)。至少这是我的看法。
      • 如果是文件流,并且已经到达文件末尾,那么应该设置eofbit。国际海事组织。如果使用文件流破坏了readsome 的语义,那么readsome 不应该可用于文件流。
      【解决方案3】:

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

      【讨论】:

      • 为什么这可能有用是 this 问题:)
      【解决方案4】:

      其他人已经回答了为什么 readsome 不会按设计设置 eofbit。我将建议一种在不以直观方式设置fail 位的情况下读取一些字节直到eof 的方法,就像您尝试使用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 
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2010-10-09
        • 2016-06-08
        • 1970-01-01
        • 2012-11-19
        • 1970-01-01
        • 1970-01-01
        • 2012-03-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多