在C ++文件IO(或ios对象中的任何错误)中捕获所有错误的最佳实践

时间:2014-10-06 19:32:49

标签: c++ c++11 iostream

在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对象可能出现的错误的最佳做法是什么?

2 个答案:

答案 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)

你可以稍微减轻疼痛,因为:

  1. 您可以在打开后调用流上的例外()

  2. stream.close()隐含在析构函数

  3. -

    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()实现中将一个危险的指针返回到临时值。