在C ++中捕获文件IO期间的所有错误的最佳做法是什么?更具体地说,处理ios对象可能出现的错误的最佳实践是什么?例如,以下程序从磁盘读取文件并打印出来:
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <exception>
#include <stdexcept>
// Defines a custom exception
struct MyException : public std::exception {
std::string s;
MyException(std::string s_) : s(s_) {};
const char * what () const throw () {
return ("MyException: " + s).c_str();
}
};
// Prints out nested exceptions
void print_exception(std::exception const & e, size_t const level = 0) {
std::cerr << std::string(level, ' ') << "exception: " << e.what() << '\n';
try {
std::rethrow_if_nested(e);
} catch(const std::exception& e) {
print_exception(e, level+1);
} catch(...) {}
}
// Read the specified filename into a string
std::string read_into_string(std::string const & fname) {
// Create the file stream
std::ifstream fin;
fin.exceptions(std::ifstream::failbit | std::ifstream::badbit);
// Open the file
try {
fin.open(fname.c_str());
} catch(...) {
std::throw_with_nested(MyException(
"Unable to open the file: " + fname));
}
// Make sure to close out the file if there's a problem
try {
// Create the string stream
std::stringstream sin;
sin.exceptions(std::ifstream::failbit | std::ifstream::badbit);
// Put the file stream into a string stream
try {
sin << fin.rdbuf();
} catch(...) {
std::throw_with_nested(MyException(
"Error when pusing the file stream into the string stream"));
}
// Turn the string stream into a string
std::string str;
try {
str = sin.str();
} catch(...) {
std::throw_with_nested(MyException(
"Error converting the string stream into a string"));
}
// Close out the file
fin.close();
// Return the string;
return str;
} catch(...) {
// Close out the file
fin.close();
// Rethrow the exception
throw;
}
}
int main() {
try {
std::string str(read_into_string("file.txt"));
std::cout << str;
} catch(const std::exception& e) {
print_exception(e);
}
}
然而,它似乎非常非常沉重。基本上,似乎我们每次触摸ios对象时都必须检查,因为可能出现问题,这将有助于确切知道在哪里。此外,上面的代码包含多个文件关闭,一个包含所有内容,一个包含异常,这是不可取的。最后,我没有检查其他ios对象(如cout)的错误状态,但从技术上来说,因为它们是ios对象,它们是否也不能设置一个坏的或失败的位应该被困?如果出现错误,是否需要关闭字符串流?
真的,核心问题是:处理ios对象可能出现的错误的最佳做法是什么?
答案 0 :(得分:4)
在C ++中对I / O流启用.exceptions()
并不常见。很可能你学到了一些其他语言,他们教你如何尽可能地使用例外。 唐&#39;吨
它&#39;非常容易处理流上的错误,没有例外:流将从真实变为虚假。此外,除非您重置故障位,否则对falsy流的任何操作都将完全没有效果。
此外,还有一种方法可将整个输入流转储到输出流中。
// Read the specified filename into a string
std::string read_into_string(std::string const & fname) {
// Create the file stream
std::ifstream fin(fname.c_str());
std::ostringstream oss;
oss << fin.rdbuf();
if (!fin) throw MyException();
return oss.str();
}
但是,您可能需要重新考虑单个流中输入的需求。通常我会发现一系列线条更有用。
答案 1 :(得分:2)
你可以稍微减轻疼痛,因为:
您可以在打开后调用流上的例外()
stream.close()隐含在析构函数
-
std::string read_into_string(std::string const & fname) {
// Create the file stream
std::ifstream fin(fname.c_str());
try {
fin.exceptions(std::ifstream::failbit | std::ifstream::badbit);
} catch(...) {
std::throw_with_nested(MyException(
"Unable to open the file: " + fname));
}
// Create the string stream
std::stringstream sin;
try {
sin.exceptions(std::ifstream::failbit | std::ifstream::badbit);
sin << fin.rdbuf();
} catch(...) {
std::throw_with_nested(MyException(
"Error when pusing the file stream into the string stream"));
}
// this try is very pedantic, you probably don't need it since the only exception
// here will be std::bad_alloc
try {
return sin.str();
} catch(...) {
std::throw_with_nested(MyException(
"Error converting the string stream into a string"));
}
// RAII takes care of closing all the file handles for you
}
然而,大多数人会更喜欢写这个函数:
std::string read_into_string(std::string const & fname) {
try {
std::ifstream fin(fname.c_str());
fin.exceptions(std::ifstream::failbit | std::ifstream::badbit);
std::stringstream sin;
sin.exceptions(std::ifstream::failbit | std::ifstream::badbit);
sin << fin.rdbuf();
return sin.str();
} catch(...) {
std::throw_with_nested(MyException(
std::string("problem with file ") + fname));
}
}
另外,MyException应该更像是这样:
struct MyException : public std::runtime_error {
MyException(const std::string& s_) : std::runtime_error(std::string("MyException:) +s_) {};
};
因为这种方式它是从runtime_error(它实际上是什么)得出的,并且你没有在what()实现中将一个危险的指针返回到临时值。