gpt4 book ai didi

c++ - 管道传输到/dev/null 时出现未指定的 iostream_category 错误(ios::badbit 问题?)

转载 作者:太空狗 更新时间:2023-10-29 21:36:19 24 4
gpt4 key购买 nike

我有解析配置文件的代码,如果出现错误,它可能会将输出发送到 stdout 或 stderr。

不幸的是,当我将程序的输出通过管道传输到/dev/null 时,出现异常:ios_base::clear: unspecified iostream_category error带有标准错误 Inappropriate ioctl for device .

这是我的代码的相关部分:

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

这是一个异常的例子:

~> ./Application test1.conf > /dev/null
test1.conf: # this is a line in the config file
Caught exception ios_base::clear: unspecified iostream_category error

当我不通过管道传输到 /dev/null 时(但对于标准输出或常规文件),一切都很好。我首先怀疑是 cout 和 cerr 引起了问题,但我不确定。

我终于发现我可以通过在打开文件后启用这一行来解决这个问题,从而忽略 badbit 类型的异常。

file.exceptions(std::ios::failbit);

坦率地说,我是 C++ 的新手,无法理解这里发生了什么。

我的问题:是什么导致了 unspecified iostream_category异常(exception)?我怎样才能避免它?正在设置 file.exceptions(std::ios::failbit);确实是一个合适的解决方案,还是会带来其他陷阱? (非常感谢指向一个详细说明在 C++ 中打开文件的最佳实践的良好来源的指针,其中确实包括所有正确的异常处理或一些背景解释!)

最佳答案

我推荐以下方法。这是基于我自己的经验以及上面提供的一些链接。简而言之,我建议如下:

  1. 在使用 C++ 流时不要打开异常。它们很难正确,我发现它们降低了我的代码的可读性,这有点违背了异常的目的。 (恕我直言,如果 C++ 流默认以更合理的方式使用异常会更好。但由于它不是以这种方式构建的,所以最好不要强行解决这个问题,而只是遵循设计者似乎拥有的模式记住。)
  2. 相信 getline 会正确处理各种流位。如果设置了坏位或失败位,则无需在每次调用后进行检查。发生这些情况时,getline 返回的流将隐式转换为 false。
  3. 重组您的代码以遵循 RAII 模式,这样您就无需手动调用 open() 或 close()。这不仅简化了您的代码,而且确保您不会忘记关闭它。如果您不熟悉 RAII 模式,请参阅 https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization .
  4. 不要重复将 errno 或您的文件名之类的东西放入您生成的异常中。尽量减少它们以避免重复,并在底部使用 catch block 来处理错误,可能会抛出一个新的异常,添加您希望报告的详细信息。

话虽如此,我建议重写您的代码,使其看起来像下面这样:

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++ - 管道传输到/dev/null 时出现未指定的 iostream_category 错误(ios::badbit 问题?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40834740/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com