Getting failbit when reading from txt file ending in new line

Viewed 126

I am trying to read from a simple text file in a specific order. In this case I read an int, then string, then double. Here is the text file:

54321 Television
250
46782 Laptop
1200
23461 Ipad
500
87612 Playstation
400

Here is the function I am using to check the file for errors (wrong types or wrong filename):

void Electronics::ReadData(istream& electronicsFile)
{
    int barcode;
    string name;
    double price;

    while (electronicsFile.good()) {
        electronicsFile >> barcode >> name >> price;
    }
    if (electronicsFile.fail()) {
        throw runtime_error("Error reading electronics file");
    }
}

The issue that I am running into is that my text file ends with a newline, causing my program to throw the runtime error every time it reads the file to the end. How can I read the entire file and not have it return a failbit?

2 Answers

First, your loop condition should be the input operation itself.

while (electronicsFile >> barcode >> name >> price) {
  ; // empty body
}

After this, electronicsFile.fail() will always be true after the loop has finished. The only way the loop exits is if there was an invalid read or if it reached the end of the file.

So you should change your condition to only throw if it didn't reach the end of the file.

if (!electronicsFile.eof()) {
  throw runtime_error("Error reading electronics file");
}

The issue that I am running into is that my text file ends with a newline

That's not a problem. When you try to read elements for as long as you can (and to do that, your code should rather be while (electronicsFile >> barcode >> name >> price);), you will encounter the set failbit, because that's what stopping you from reading from electronicsFile indefinitely. You can always clear() the stream though.

If you really care to check whether the file is incorrect while reading, you may check every read statement separately plus *checking for .eof(), not .fail():

int main() {
   std::fstream electronicsFile {"logi.txt"};

   int barcode = 0;
   std::string name;
   double price = 0.0;

   try {
       for (auto c = electronicsFile.get(); !electronicsFile.eof();) {
           electronicsFile.putback(c);
           const auto message = "Error reading electronics file";
           if (!(electronicsFile >> barcode)) throw std::runtime_error(message);
           if (!(electronicsFile >> name)) throw std::runtime_error(message);
           if (!(electronicsFile >> price)) throw std::runtime_error(message);
       }
   } catch (const std::runtime_error& ex) {
       std::cerr << ex.what();
   }
}

This will correctly flow through the execution process for the correct data stored in the file and will abort plus print the exception message in case of the incorrect file data.

Related