为什么在读取时发现eof时设置了故障位? [英] Why is failbit set when eof is found on read?

查看:92
本文介绍了为什么在读取时发现eof时设置了故障位?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已阅读< fstream> 早于< exception> 。忽略 fstream 上的异常信息不足的事实,我有以下问题:

I've read that <fstream> predates <exception>. Ignoring the fact that exceptions on fstream aren't very informative, I have the following question:

使用 exceptions()方法在文件流上启用例外。

It's possible to enable exceptions on file streams using the exceptions() method.

ifstream stream;
stream.exceptions(ifstream::failbit | ifstream::badbit);
stream.open(filename.c_str(), ios::binary);

任何尝试打开不存在的文件,没有正确权限的文件或任何其他I / O的尝试问题将导致异常。使用断言的编程风格非常好。该文件应该在那儿并且可读。如果不满足条件,我们将有例外。如果不确定文件是否可以安全打开,可以使用其他功能进行测试。

Any attempt to open a nonexistent file, a file without the correct permissions, or any other I/O problem will results in exception. This is very good using an assertive programming style. The file was supposed to be there and be readable. If the conditions aren't met, we get an exception. If I wasn't sure whether the file could safely be opened, I could use other functions to test for it.

但是现在假设我尝试读入缓冲区,像这样:

But now suppose I try to read into a buffer, like this:

char buffer[10];
stream.read(buffer, sizeof(buffer)); 

如果流在填充缓冲区之前检测到文件结束,则流决定设置故障位,如果启用了它们,则会引发异常。为什么?这有什么意义?我本来可以验证,只需在读取后测试 eof()

If the stream detects the end-of-file before filling the buffer, the stream decides to set the failbit, and an exception is fired if they were enabled. Why? What's the point of this? I could have verified that just testing eof() after the read:

char buffer[10];
stream.read(buffer, sizeof(buffer));
if (stream.eof()) // or stream.gcount() != sizeof(buffer)
    // handle eof myself

此设计选择使我无法在流上使用标准异常,并迫使我针对权限或I / O错误创建自己的异常处理。还是我错过了什么?有没有出路?例如,我可以轻松地测试是否可以在流中读取 sizeof(buffer)个字节之前吗?

This design choice prevents me from using standard exceptions on streams and forces me to create my own exception handling on permissions or I/O errors. Or am I missing something? Is there any way out? For example, can I easily test if I can read sizeof(buffer) bytes on the stream before doing so?

推荐答案

为改进@absence的答案,它遵循方法 readeof(),其方法与 read()相同。 ,但未在EOF上设置故障位。还测试了实际的读取失败,例如由于硬移除USB记忆棒或网络共享访问中的链接断开而导致传输中断。已在Windows 7的VS2010和VS2013以及Linux的gcc 4.8.1上进行了测试。

Improving @absence's answer, it follows a method readeof() that does the same of read() but doesn't set failbit on EOF. Also real read failures have been tested, like an interrupted transfer by hard removal of a USB stick or link drop in a network share access. It has been tested on Windows 7 with VS2010 and VS2013 and on linux with gcc 4.8.1. On linux only USB stick removal has been tried.

#include <iostream>
#include <fstream>
#include <stdexcept>

using namespace std;

streamsize readeof(istream &stream, char *buffer, streamsize count)
{
    streamsize offset = 0;
    streamsize reads;
    while (!stream.eof())
    {
        // Check also for already failed streams
        if (stream.fail())
            throw runtime_error("Stream I/O error while reading");

        if (count == 0)
            return offset;

        // 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)
        reads = stream.rdbuf()->sgetn(buffer + offset, count);

        // This rarely sets failbit on VS2010 and VS2013 (Windows 7) on read
        // failure of the previous sgetn()
        (void)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();

        offset += reads;
        count -= reads;
    }

    return offset;
}

#define BIGGER_BUFFER_SIZE 200000000

int main(int argc, char* argv[])
{
    ifstream stream;
    stream.exceptions(ifstream::badbit | ifstream::failbit);
    stream.open("<big file on usb stick>", ios::binary);

    char *buffer = new char[BIGGER_BUFFER_SIZE];

    streamsize reads = readeof(stream, buffer, BIGGER_BUFFER_SIZE);

    if (stream.eof())
        cout << "eof" << endl << flush;

    delete buffer;

    return 0;
}

底线:在Linux上,行为更加一致和有意义。如果对实际读取失败启用了例外,它将抛出 sgetn()。相反,Windows通常将读取失败视为EOF。

Bottom line: on linux the behavior is more consistent and meaningful. With exceptions enabled on real read failures it will throw on sgetn(). On the contrary Windows will treat read failures as EOF most of the times.

这篇关于为什么在读取时发现eof时设置了故障位?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆