[英]When does `ifstream::readsome` set `eofbit`?
这段代码永远循环:
#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
。
通过修改内部状态标志来发出错误信号:
eofbit
调用函数时,get指针位于流缓冲区内部输入数组的末尾,这意味着内部缓冲区中没有要读取的位置(可能是输入序列的末尾)。 当rdbuf()->in_avail()
在提取第一个字符之前返回-1
时会发生这种情况。
failbit
在调用函数之前,流位于字符源的末尾。
badbit
上面发生的错误发生了。
几乎相同,标准说:
[C++11: 27.7.2.3]:
streamsize readsome(char_type* s, streamsize n);
32.效果:表现为无格式输入函数(如27.7.2.3第1段所述)。 构造一个sentry对象之后,如果
!good()
调用setstate(failbit)
,它可能抛出一个异常,然后返回。 否则,提取字符并将它们存储到数组的连续位置,该数组的第一个元素由s
指定。 如果rdbuf()->in_avail() == -1
,则调用setstate(eofbit)
(可能抛出ios_base::failure
(27.5.5.4)),并且不提取任何字符;
- 如果
rdbuf()->in_avail() == 0
,则不提取任何字符- 如果
rdbuf()->in_avail() > 0
,则提取min(rdbuf()->in_avail(),n))
。33.返回:提取的字符数。
in_avail() == 0
条件是无操作意味着如果流缓冲区为空, ifstream::readsome
本身是无操作,但in_avail() == -1
条件意味着它将设置eofbit
其他一些操作导致了in_avail() == -1
。
这似乎是一种不一致,即使尽管是readsome
的“某种”性质。
那么,什么是语义readsome
和eof
? 我是否正确地解释了它们? 它们是流库中设计不佳的一个例子吗?
(从[IMO]无效的libstdc ++ bug 52169中窃取 。)
我认为这是一个自定义点,默认流实现并没有真正使用它。
in_avail()
返回它在内部缓冲区中可以看到的字符数(如果有的话)。 否则,它会调用showmanyc()
来尝试检测是否已知其他字符可用,因此保证缓冲区填充请求成功。
反过来, showmanyc()
将返回它知道的字符数(如果有),如果它知道读取将失败则返回-1,如果它没有线索则返回0。
默认实现( basic_streambuf
)总是返回0,所以这是你得到的,除非你有一个流与其他streambuf重写showmanyc
。
你的循环本质上就像你所知道的那样是多个字符,并且当它为零时(意味着“不确定”)它会被卡住。
其他人已回答为什么readsome
不会设计eofbit
。 我将建议一种方法来读取一些字节,直到eof
而不用直观的方式设置fail
位,就像你尝试使用readsome
。 这是另一个问题研究的结果。
#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
}
}
如果没有可用的字符(即std:streambuf
gptr() == egptr()
showhowmanyc()
则调用虚拟成员函数showhowmanyc()
。 我可以有一个showmanyc()
的实现,它返回一个错误代码。 为什么这可能有用是一个不同的问题。 但是,这可以设置eof()
。 当然, in_avail()
意味着不会失败,也不会阻止并只返回已知可用的字符。 也就是说,除非你有一个相当奇怪的流缓冲区,否则你上面的循环基本上保证是一个无限循环。
我不认为readome()是针对您尝试做的(从磁盘上的文件读取)...来自cplusplus.com:
该函数旨在用于从某些类型的异步源读取二进制数据,这些数据可能等待更多字符,因为它会在本地缓冲区耗尽时停止读取,从而避免潜在的意外延迟。
所以听起来像readsome()用于来自网络套接字或类似的流,你可能只想使用read()。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.