为什么在读取时找到 eof 时设置故障位
Why is failbit set when eof is found on read?
我读过<fstream>
早于<exception>
。忽略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)
字节?
故障位旨在允许流报告某些操作未能成功完成。 这包括无法打开文件、尝试读取不存在的数据以及尝试读取错误类型的数据等错误。
您询问的特定案例在此处转载:
char buffer[10];
stream.read(buffer, sizeof(buffer));
您的问题是,为什么在读取所有输入之前到达文件末尾时设置故障位。 原因是这意味着读取操作失败 - 您要求读取 10 个字符,但文件中没有足够的字符。 因此,操作未成功完成,并且流会发出故障信号以通知您这一点,即使将读取可用字符也是如此。
如果要执行读取操作,其中最多要读取一定数量的字符,可以使用readsome
成员函数:
char buffer[10];
streamsize numRead = stream.readsome(buffer, sizeof(buffer));
此函数将读取字符直到文件末尾,但与read
不同的是,如果在读取字符之前到达文件末尾,它不会设置 failbit。 换句话说,它说"尝试阅读这么多字符,但如果你不能,这不是错误。 只要让我知道你读了多少书。 这与read
形成鲜明对比,后者说"我想要这么多字符,如果你做不到,那就是错误。
编辑:我忘了提到的一个重要细节是,可以在不触发故障位的情况下设置eofbit。 例如,假设我有一个包含文本的文本文件
137
之后没有任何换行符或尾随空格。 如果我写这段代码:
ifstream input("myfile.txt");
int value;
input >> value;
此时input.eof()
将返回 true,因为当从文件中读取字符时,流会到达文件的末尾,试图查看流中是否有任何其他字符。 但是,input.fail()
不会返回 true,因为操作成功 - 我们确实可以从文件中读取一个整数。
希望这有帮助!
直接使用底层缓冲区似乎可以解决问题:
char buffer[10];
streamsize num_read = stream.rdbuf()->sgetn(buffer, sizeof(buffer));
改进@absence的答案,它遵循一种方法readeof()
该方法对read()
执行相同的操作,但不在 EOF 上设置故障位。还测试了真正的读取失败,例如通过硬移除 U 盘或网络共享访问中的链接丢弃而导致传输中断。它已经在带有VS2010和VS2013的Windows 7以及带有gcc 4.8.1的Linux上进行了测试。在 Linux 上,只尝试了移除 U 盘。
#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。
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- 针对代码::块设置的 SFML 故障排除
- 为什么parse_config_file流上设置故障位
- 什么可能导致故障位被设置在ofsream::open上
- 在 C++ 中立即设置故障位(在读取任何字节之前)
- 故障位被设置为ifstream对象,原因是什么
- 获取设置故障位的流位置 / std::ios::抛出故障
- 是否可以在 C++11 中手动设置 istream 故障位
- Taglib设置专辑图片,但给出了SEG故障
- IOstream哨兵在到达末尾时不会设置故障位
- STD上的SEG故障::设置整数插入
- 当 seekg 查找文件末尾时未设置故障位 (C++,Linux)
- 设置检测插入故障
- std::cout 可以设置坏位或故障位与操作员<<吗?如果是,什么时候?
- fstream.open()设置故障位
- 为什么设置故障位.文件似乎打印得很好
- 当设置bool时可能出现段故障
- 为什么在读取时找到 eof 时设置故障位
- STL设置内部映射,奇怪的seg故障和Valgrind分析
- IF流运算符>> uint16_t设置故障位