为什么读取时发现 eof 时会设置失败位?

发布于 2024-11-25 12:11:50 字数 973 浏览 2 评论 0原文

我读到 早于 。忽略 fstream 上的异常信息不多这一事实,我有以下问题:

可以使用 exceptions() 方法在文件流上启用异常。

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

任何尝试打开不存在的文件、没有正确权限的文件或任何其他 I/O 问题都将导致异常。使用自信的编程风格非常好。该文件应该存在并且可读。如果不满足条件,我们就会得到一个例外。如果我不确定文件是否可以安全打开,我可以使用其他函数来测试它。

但现在假设我尝试读入缓冲区,如下所示:

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

如果流在填充缓冲区之前检测到文件结尾,则流决定设置 failbit,并且如果满足以下条件,则会引发异常:他们被启用了。为什么?这有什么意义呢?我可以验证在读取后仅测试 eof()

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

这种设计选择阻止我在流上使用标准异常,并迫使我创建自己的权限或 I/O 错误异常处理。或者我错过了什么?还有出路吗?例如,我可以在执行此操作之前轻松测试是否可以读取流上的 sizeof(buffer) 字节吗?

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

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);

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)); 

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

狠疯拽 2024-12-02 12:11:50

故障位旨在允许流报告某些操作未能成功完成。这包括诸如无法打开文件、尝试读取不存在的数据以及尝试读取错误类型的数据等错误。

您所询问的特定情况在这里重印:

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

您的问题是为什么在读取所有输入之前到达文件末尾时设置失败位。原因是,这意味着读取操作失败 - 您要求读取 10 个字符,但文件中没有足够多的字符。因此,操作未成功完成,并且流会发出失败位信号以让您知道这一点,即使将读取可用字符也是如此。

如果您想要执行读取操作,想要读取最多一定数量的字符,则可以使用readsome成员函数:

char buffer[10];
streamsize numRead = stream.readsome(buffer, sizeof(buffer)); 

该函数将读取最多字符数文件末尾,但与 read 不同,如果在读取字符之前到达文件末尾,则它不会设置失败位。换句话说,它表示“尝试读取这么多字符,但如果您不能读取,也不是错误。请告诉我您读取了多少字符。”这与 read 形成鲜明对比,后者表示“我想要精确这么多字符,如果你做不到,那就是一个错误。”

编辑:我忘记提及的一个重要细节是可以在不触发failbit的情况下设置eofbit。例如,假设我有一个文本文件,其中包含文本,

137

之后没有任何换行符或尾随空格。如果我写这段代码:

ifstream input("myfile.txt");

int value;
input >> value;

那么此时 input.eof() 将返回 true,因为当从文件中读取字符时,流会到达文件末尾,试图查看是否还有其他字符流中的字符。然而,input.fail()不会返回true,因为操作成功了——我们确实可以从文件中读取一个整数。

希望这有帮助!

The failbit is designed to allow the stream to report that some operation failed to complete successfully. This includes errors such as failing to open the file, trying to read data that doesn't exist, and trying to read data of the wrong type.

The particular case you're asking about is reprinted here:

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

Your question is why failbit is set when the end-of-file is reached before all of the input is read. The reason is that this means that the read operation failed - you asked to read 10 characters, but there weren't sufficiently many characters in the file. Consequently, the operation did not complete successfully, and the stream signals failbit to let you know this, even though the available characters will be read.

If you want to do a read operation where you want to read up to some number of characters, you can use the readsome member function:

char buffer[10];
streamsize numRead = stream.readsome(buffer, sizeof(buffer)); 

This function will read characters up to the end of the file, but unlike read it doesn't set failbit if the end of the file is reached before the characters are read. In other words, it says "try to read this many characters, but it's not an error if you can't. Just let me know how much you read." This contrasts with read, which says "I want precisely this many characters, and it's an error if you can't do it."

EDIT: An important detail I forgot to mention is that eofbit can be set without triggering failbit. For example, suppose that I have a text file that contains the text

137

without any newlines or trailing whitespace afterwards. If I write this code:

ifstream input("myfile.txt");

int value;
input >> value;

Then at this point input.eof() will return true, because when reading the characters from the file the stream hit the end of the file trying to see if there were any other characters in the stream. However, input.fail() will not return true, because the operation succeeded - we can indeed read an integer from the file.

Hope this helps!

小红帽 2024-12-02 12:11:50

直接使用底层缓冲区似乎可以解决问题:

char buffer[10];
streamsize num_read = stream.rdbuf()->sgetn(buffer, sizeof(buffer));

Using the underlying buffer directly seems to do the trick:

char buffer[10];
streamsize num_read = stream.rdbuf()->sgetn(buffer, sizeof(buffer));
春风十里 2024-12-02 12:11:50

改进@absence的答案,它遵循一个方法readeof(),该方法执行与read()相同的操作,但不会在EOF上设置failbit。还测试了真实的读取失败,例如硬拔除 USB 记忆棒或网络共享访问中的链接丢失导致的传输中断。它已在使用 VS2010 和 VS2013 的 Windows 7 以及使用 gcc 4.8.1 的 Linux 上进行了测试。在 Linux 上,仅尝试过 USB 棒移除。

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

using namespace std;

streamsize readeof(istream &stream, char *buffer, streamsize count)
{
    if (count == 0 || stream.eof())
        return 0;

    streamsize offset = 0;
    streamsize reads;
    do
    {
        // 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
        (void)stream.peek();

        if (stream.fail())
            throw runtime_error("Stream I/O error while reading");

        offset += reads;
        count -= reads;
    } while (count != 0 && !stream.eof());

    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。

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)
{
    if (count == 0 || stream.eof())
        return 0;

    streamsize offset = 0;
    streamsize reads;
    do
    {
        // 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
        (void)stream.peek();

        if (stream.fail())
            throw runtime_error("Stream I/O error while reading");

        offset += reads;
        count -= reads;
    } while (count != 0 && !stream.eof());

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文