11

std::getline throws exception when it gets an eof. this is how I am doing.

std::ifstream stream;
stream.exceptions(std::ifstream::failbit|std::ifstream::badbit);
try{
  stream.open(_file.c_str(), std::ios_base::in);
}catch(std::ifstream::failure e){
  std::cout << "Failed to open file " << _file.c_str() << " for reading" << std::endl;
}
while(!stream.eof()){
  std::string buffer = "";
  std::getline(stream, buffer);
  //process buffer
  //I do also need to maintain state while parsing
}

In the above code getline is throwing exception as it gets eof How to handle this situation ?

EDIT

std::string buffer = "";
while(std::getline(stream, buffer)){
    //also causes getline to hit eof and throw
}
Dipro Sen
  • 3,590
  • 11
  • 34
  • 50
  • 1
    Side note: the structure of the loop is incorrect as you need to check `eof()` immediately after the `std::getline()`. – hmjd Aug 04 '12 at 10:26
  • 3
    You are asking for an exception. After hitting `eof`, the stream is in a `bad` state. – Bo Persson Aug 04 '12 at 10:26
  • 3
    [Why is iostream::eof inside a loop condition considered wrong?](http://stackoverflow.com/q/5605125/500104) – Xeo Aug 04 '12 at 10:27
  • 'How to handle this situation'. That depends on what you want to do, you haven't explained yourself very well. Do you want to not have an exception? Do you want to catch the exception, and if you want to catch it what do you want to happen next? Please explain what you are trying to do. – jahhaj Aug 04 '12 at 10:59

2 Answers2

14

You activate the exception handling of your stream at the very beginning of your code:

stream.exceptions(std::ifstream::failbit|std::ifstream::badbit);

Now if the extraction of formatted data such as floating-point values, integers or strings will fail, it will set the failbit:

eofbit    indicates that an input operation reached the end of an 
          input sequence;
failbit   indicates that an input operation failed to read the expected 
          characters, or that an output operation failed to generate the 
          desired characters.

While getline(stream,buffer) will indeed set the eofbit if it reaches the end of a file, it will also set the failbit, since the desired characters (a line) couldn't be extracted.

Either wrap another try-catch-block around your loop or disable the failbit exception.

Example:

#include <iostream>
#include <fstream>

int main(){
  std::ifstream stream("so.cc");
  stream.exceptions(std::ifstream::failbit|std::ifstream::badbit);
  std::string str;

  try{
    while(std::getline(stream, str));
  }catch(std::ifstream::failure e){
    std::cerr << "Exception happened: " << e.what() << "\n"
      << "Error bits are: "
      << "\nfailbit: " << stream.fail() 
      << "\neofbit: " << stream.eof()
      << "\nbadbit: " << stream.bad() << std::endl;    
  }
  return 0;
}

Result:

Exception happened: basic_ios::clear
Error bits are:
failbit: 1
eofbit: 1
badbit: 0

Note that both eofbit and failbit are set.

See also:

Zeta
  • 95,453
  • 12
  • 173
  • 214
  • So should I place a silent catch block around `getline` ? – Dipro Sen Aug 04 '12 at 12:31
  • 1
    If you use `getline(std::istream,std::string)`, then the only reason for it to fail to extract the string will be an EOF. I suggest you to actually disable throwing an exception on `std::ifstream::failbit`. Of course you can use a silent catch block. There are many ways to handle such things, choose the one that suits you best. – Zeta Aug 04 '12 at 13:11
  • I want the `getline` read the stream into buffer as long as it doesn't fetch `eof. once it fails due to `eof` I want the control to come below `getline` because I've states of previous lines kept previous iterations. – Dipro Sen Aug 04 '12 at 18:47
0

As you said, calling getline when the stream is currently positioned on the blank line at the end of the file will set both eofbit (because it reaches the end of file) and failbit (because nothing could be extracted, not even the line delimiter).

So, if you have set the stream to throw exceptions when failbit is set, you get an exception thrown when reading that blank line.

To prevent that, you can peek for the next character on the stream before calling getline:

std::string buffer = "";
while(stream.peek() != EOF && std::getline(stream, buffer)){
    //do something with the buffer
}