C++ 管道传输到/dev/null时出现未指定的iostream_类别错误(ios::badbit问题?)
我有解析配置文件的代码,如果出现错误,它可能会将输出发送到stdout或stderr 不幸的是,当我将程序的输出管道连接到/dev/null时,我得到了一个异常:C++ 管道传输到/dev/null时出现未指定的iostream_类别错误(ios::badbit问题?),c++,exception,io,std,C++,Exception,Io,Std,我有解析配置文件的代码,如果出现错误,它可能会将输出发送到stdout或stderr 不幸的是,当我将程序的输出管道连接到/dev/null时,我得到了一个异常:ios_base::clear:unspecified iostream_category error,其中stderror设备的ioctl不合适 以下是我的代码的相关部分: try { file.exceptions(std::ios::failbit | std::ios::badbit); file.open(co
ios_base::clear:unspecified iostream_category error
,其中stderror设备的ioctl不合适
以下是我的代码的相关部分:
try {
file.exceptions(std::ios::failbit | std::ios::badbit);
file.open(config_file);
// file.exceptions(std::ios::failbit);
}
catch (std::ios_base::failure& e) {
throw std::invalid_argument(std::string("Can't open config file ") + config_file + ": " + strerror(errno));
}
try {
errno = 0; // reset errno before I/O operation.
// ...
while (std::getline(file, line)) {
if ( [... unknown line ...] ) {
std::cerr << "Invalid line in config " << config_file << ": " << line << std::endl;
continue;
}
// debug info to STDOUT:
std::cout << config_file << ": " << line << std::endl;
}
} catch (std::ios_base::failure& err) {
std::cout << "Caught exception " << err.what() << std::endl;
if (errno != 0) {
char* theerror = strerror(errno);
file.close();
throw std::invalid_argument(std::string("Can't read config file ") + config_file + ": " + theerror);
}
}
try {
file.close();
}
catch (std::ios_base::failure& e) {
throw std::invalid_argument(std::string("Can't close config file ") + config_file + ": " + strerror(errno));
}
当我不使用管道连接到/dev/null
(而是连接到标准输出或常规文件)时,一切都很好。我首先怀疑cout和cerr在哪里引起了问题,但我不确定
我最终发现,我可以通过在打开文件后启用这一行来解决这个问题,从而忽略badbit类型的异常
file.exceptions(std::ios::failbit);
坦白地说,我在C++方面太新手了,无法理解这里发生了什么。
我的问题:是什么导致了未指定的iostream\u类别异常?我怎样才能避免呢?正在设置
file.exceptions(std::ios::failbit)代码>确实是一个正确的解决方案,还是会带来其他陷阱?(一个指向一个好的源代码的详细说明,详细说明了在C++中打开文件的最佳实践,其中包括所有适当的异常处理,或者一些背景说明,非常感谢!) < P>我推荐下面的方法。这是基于我自己的经验以及上面提供的一些链接。总之,我建议如下:
using namespace std;
try {
errno = 0;
// Construct the stream here (part of RAII) then you don't need to call
// open() or close() manually.
ifstream file(config_file);
if (!file.is_open()) {
throw invalid_argument("Could not open the file");
}
while (getline(file, line)) {
// do all your processing as if no errors will occur (getline will
// be explicitly cast to a false when an error occurs). If there is
// something wrong with a line (bad format, etc.) then throw an
// exception without echoing it to cerr.
}
if (file.bad()) {
throw invalid_argument("Problem while reading file");
}
}
catch (const invalid_argument& e) {
// Whatever your error handling needs to be. config_file should still
// be valid so you can add it here, you don't need to add it to each
// individual exception. Also you can echo to cerr here, you don't need
// to do it inside the loop. If you want to use errno it should still
// be set properly at this point. If you want to pass the exception to
// the next level you can either rethrow it or generate a new one that
// adds additional details (like strerror and the filename).
}
使用C++流时不要打开异常。它们是如此难以正确,我发现它们使我的代码可读性降低,这有点违背了异常的目的。(IHO,如果C++流默认情况下使用异常,并且更合理的方式会更好。但是,由于它不是以这样的方式构建的,所以最好不要强制这个问题,而是按照设计者们似乎已经想到的模式。)
依靠getline将正确处理各种流位这一事实。您不需要在每次调用后检查是否设置了坏位或失败位。当发生这些情况时,getline返回的流将隐式转换为false
按照RAII模式重新构造代码,这样就不需要手动调用open()或close()。这不仅简化了代码,而且确保您不会忘记关闭它。如果您不熟悉RAII模式,请参阅
不要反复将errno或文件名之类的内容放入生成的异常中。尽量减少错误以避免重复,并在底部使用catch块来处理错误,可能会抛出一个新的异常,添加您希望报告的详细信息
话虽如此,我建议您重新编写代码,使其看起来如下所示:
using namespace std;
try {
errno = 0;
// Construct the stream here (part of RAII) then you don't need to call
// open() or close() manually.
ifstream file(config_file);
if (!file.is_open()) {
throw invalid_argument("Could not open the file");
}
while (getline(file, line)) {
// do all your processing as if no errors will occur (getline will
// be explicitly cast to a false when an error occurs). If there is
// something wrong with a line (bad format, etc.) then throw an
// exception without echoing it to cerr.
}
if (file.bad()) {
throw invalid_argument("Problem while reading file");
}
}
catch (const invalid_argument& e) {
// Whatever your error handling needs to be. config_file should still
// be valid so you can add it here, you don't need to add it to each
// individual exception. Also you can echo to cerr here, you don't need
// to do it inside the loop. If you want to use errno it should still
// be set properly at this point. If you want to pass the exception to
// the next level you can either rethrow it or generate a new one that
// adds additional details (like strerror and the filename).
}
我在前面的答案的基础上做了改进,编写了两个函数,使用lambdas处理流检查,以实际处理文件。这有以下优点:
您不会忘记将流检查放在哪里
您的代码集中于您想要做的事情(文件处理),而不是系统样板项目
我已经创建了两个版本。第一次给你的lambda流,你可以随心所欲地处理它。第二次,你的lambda一次只给一行。在这两种情况下,如果发生I/O问题,它将抛出系统错误异常。您还可以在lambda中抛出自己的异常,它们将被正确地传递
namespace {
inline void throwProcessingError(const string& filename, const string& what_arg) {
throw system_error(errno, system_category(), what_arg + " " + filename);
}
}
void process_file(const string& filename, function<void (ifstream &)> fn) {
errno = 0;
ifstream strm(filename);
if (!strm.is_open()) throwProcessingError(filename, "Failed to open");
fn(strm);
if (strm.bad()) throwProcessingError(filename, "Failed while processing");
}
void process_file_line_by_line(const string& filename, function<void (const string &)> fn)
{
errno = 0;
ifstream strm(filename);
if (!strm.is_open()) throwProcessingError(filename, "Failed to open");
string line;
while (getline(strm, line)) {
fn(line);
}
if (strm.bad()) throwProcessingError(filename, "Failed while processing");
}
或
我仍然希望得到一个好的答案,但我发现的最接近的是一个相关的问题,以及一篇由Jan Philip Gehrcke撰写的文章的链接。
process_file("myfile.txt", [](ifstream& stream) {
... my processing on stream ...
});
process_file_line_by_line("myfile.txt", [](const string& line) {
... process the line ...
});