-->

Input stream iterators and exceptions

2019-06-24 00:54发布

问题:

I was playing around with istream iterators and exception handling a few days ago and I came across with this curiousity:

#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>

using namespace std;

int main(int argc, char* argv[])
{
   if (argc < 2) {
      cout << argv[0] << " <file>" << endl;
      return -1;
   }

   try {
      ifstream ifs(argv[1]);
      ifs.exceptions(ios::failbit | ios::badbit);
      istream_iterator<string> iss(ifs), iss_end;
      copy(iss, iss_end, ostream_iterator<string>(cout, "\n"));
   }
   catch (const ios_base::failure& e) {
      cerr << e.what() << endl;
      return -2;
   }

   return 0;
}

Why a failbit exception is always raised after reading the last word of the input file?

回答1:

failbit is set whenever a read operation fails to extract any characters, whether this is because it hit EOF or not.

stringstream ss ("foo");
string s;
int i;

ss >> i; // sets failbit because there is no number in the stream
ss.clear();
ss >> s; // sets eofbit because EOF is hit
ss.clear();
ss >> s; // sets eofbit and failbit because EOF is hit and nothing is extracted.


回答2:

One detect the EOF condition by reading until a failure -- which triggers the exception -- and then checking the cause of the failure.

To expand: the istream_iterator becomes invalid when after having read a value with >>, the stream operator void* returns NULL. But for that, the operator >> has to set the fail bit, so raise the exception.



回答3:

Good question. It would be nice to be able to catch other failures in that call, but have it continue normally when it hits eof.

That said, I haven't used exceptions with streams before. I think you could do the copy and check the state of the stream afterwards to detect other errors, for example:

ifstream ifs(argv[1]);
if (!ifs) {
    cerr << "Couldn't open " << argv[1] << '\n';
    return -1;
}
//ifs.exceptions(ios::failbit | ios::badbit);
istream_iterator<std::string> iss(ifs), iss_end;
copy(iss, iss_end, ostream_iterator<std::string>(cout, "\n"));
if (!ifs.eof()) {
    cerr << "Failed to read the entire file.\n";
    return -2;
}