C++ ios::exceptions throws exception when everything is fine - c++

Here is the code from cplusplus.com/reference
#include <iostream> // std::cerr
#include <fstream> // std::ifstream
int main () {
std::ifstream file;
file.exceptions ( std::ifstream::failbit | std::ifstream::badbit );
try {
file.open ("test.txt");
while (!file.eof()) file.get();
file.close();
}
catch (std::ifstream::failure e) {
std::cerr << "Exception opening/reading/closing file\n";
}
return 0;
}
My code is very similar
int main()
{
std::vector<int> numbers;
std::vector<std::ifstream *> ifs;
std::array<std::string, 3> files = {
"f1.txt", "f2.txt", "f3.txt"
};
for (int i = 0; i < files.size(); ++i) {
std::ifstream *_ifs = new std::ifstream;
ifs.push_back(_ifs);
ifs[i]->exceptions( std::ifstream::failbit | std::ifstream::badbit );
}
try {
int n;
std::string line;
for (int i = 0; i < files.size(); ++i) {
ifs[i]->open(files[i]);
while (!ifs[i]->eof()) {
std::getline(*ifs[i], line);
std::istringstream iss(line);
while (iss >> n) {
if (n % 3 == 0 && n % 5 == 0 && n % 7 == 0)
numbers.push_back(n);
}
}
ifs[i]->close();
}
} catch (std::ifstream::failure e) {
std::cerr << "Error reading from files: " << e.what() << std::endl;
return 1;
}
for (int i = 0; i < ifs.size(); ++i)
delete ifs[i];
ifs.clear();
std::cout << "Files have been read\n";
// Do something with numbers
// ...
}
The issue is that nothing is read. Exception is thrown almost immediatly. If I comment out failbit from exceptions everything works fine, but exceptions are not thrown when the files are missing on Windows. On Ubuntu, without failbit exceptions are thrown when the files are missing, and everything is read correctly. But with failbit on Ubuntu as well exception is thrown at the beginning of reading and nothing is read. I tried to google it. Found the example from cplusplus.com . And stackoverflow question where the answer was not to check for eof, but instead read this way while(getline(ifs, line)) { /* do something with line */ } . I tried this, and got no difference. Before I did these kinds of tasks throwing user defined classes. This time I decided to try standard library for that and it seems like I am missing something.

The problem is that std::ios_base::failbit gets set when the end of the file is reached: the lines are read OK. However, once there are no further lines std::ios_base::failbit will get set: that is how the end condition is detected. As a result, only the first file is being read.
If you'd had output inside the loop reading the file you'd see that the lines are actually read. Since you filter the values read I'd guess you don't see any numbers read because none of the numbers provided matches the condition.
The check for eof() doesn't help, of course, as reading the last line will stop reading with the newline character right before reaching the end of file but it won't set std::ios_base::eofbit: the bit is only set when EOF is actually touched but that only happens with the next character read.
Since you should always check whether something was read after attempting to read, the condition while (ifs[i]->eof()) is ill-advised (and it is a good example why you should not use cplusplus.com but rather cppreference.com). Instead you should use
while (std::getline(*ifs[i], line))
You might get better results reading each of the files in their own try/catch blocks. Personally, I don't think exceptions and I/O fit well together and I have never had any production code setting an exception mask. I'd recommend staying clear of setting the exception mask for streams.

Related

getline setting failbit along with eof

I am aware of the origin of this behavior since it has been very well explained in multiple posts here in SO, some notable examples are:
Why is iostream::eof inside a loop condition considered wrong?
Use getline() without setting failbit
std::getline throwing when it hits eof
C++ istream EOF does not guarantee failbit?
And it is also included in the std::getline standard:
3) If no characters were extracted for whatever reason (not even the discarded delimiter), getline sets failbit and returns.
My question is how does one deal with this behavior, where you want your stream to catch a failbit exception for all cases except the one caused by reaching the eof, of a file with an empty last line. Is there something obvious that I am missing?
A MWE:
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
void f(const std::string & file_name, char comment) {
std::ifstream file(file_name);
file.exceptions(file.failbit);
try {
std::string line;
while (std::getline(file, line).good()) {
// empty getline sets failbit throwing an exception
if ((line[0] != comment) && (line.size() != 0)) {
std::stringstream ss(line);
// do stuff
}
}
}
catch (const std::ios_base::failure& e) {
std::cerr << "Caught an ios_base::failure.\n"
<< "Explanatory string: " << e.what() << '\n'
<< "Error code: " << e.code() << '\n';
}
}
int main() {
f("example.txt", '#');
}
where example.txt is a tab-delimited file, with its last line being only the \n char:
# This is a text file meant for testing
0 9
1 8
2 7
EDIT:
while(std::getline(file, line).good()){...} replicates the problem.
Another way to avoid setting failbit, is simply to refactor your if tests to detect the read of an empty-line. Since that is your final line in this case, you can simply return to avoid throwing the error, e.g.:
std::ifstream file (file_name);
file.exceptions (file.failbit);
try {
std::string line;
while (std::getline(file, line)) {
// detect empty line and return
if (line.size() == 0)
return;
if (line[0] != comment) {
std::stringstream ss(line);
// do stuff
}
}
}
...
You other alternative is to check whether eofbit is set in catch. If eofbit is set -- the read completed successfully. E.g.
catch (const std::ios_base::failure& e) {
if (!file.eof())
std::cerr << "Caught an ios_base::failure.\n"
<< "Explanatory string: " << e.what() << '\n'
<< "Error code: " /* << e.code() */ << '\n';
}
Edit: I misunderstood the OP, refer to David's answer above. This answer is for checking whether or not the file has a terminating newline.
At the end of your while (getline) loop, check for file.eof().
Suppose you just did std::getline() for the last line in the file.
If there is a \n after it, then std::getline() has read the delimiter and did not set eofbit. (In this case, the very next std::getline() will set eofbit.)
Whereas if there is no \n after it, then std::getline() has read EOF and did set eofbit.
In both cases, the very next std::getline() will trigger failbit and enter your exception handler.
PS: the line if ((line[0] != comment) && (line.size() != 0)) { is UB if line is empty. The conditions' order needs to be reversed.

fstream EOF unexpectedly throwing exception

My question is very similar to a previous one. I want to open and read a file. I want exceptions thrown if the file can't be opened, but I don't want exceptions thrown on EOF. fstreams seem to give you independent control over whether exceptions are thrown on EOF, on failures, and on other bad things, but it appears that EOF tends to also get mapped to the bad and/or fail exceptions.
Here's a stripped-down example of what I was trying to do. The function f() is supposed to return true if a file contains a certain word, false if it doesn't, and throw an exception if (say) the file doesn't exist.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
bool f(const char *file)
{
ifstream ifs;
string word;
ifs.exceptions(ifstream::failbit | ifstream::badbit);
ifs.open(file);
while(ifs >> word) {
if(word == "foo") return true;
}
return false;
}
int main(int argc, char **argv)
{
try {
bool r = f(argv[1]);
cout << "f returned " << r << endl;
} catch(...) {
cerr << "exception" << endl;
}
}
But it doesn't work, because basic fstream reading using operator>> is evidently one of the operations for which EOF sets the bad or the fail bit. If the file exists and does not contain "foo" the function does not return false as desired, but rather throws an exception.
The std::ios_base::failbit flag is also set when there's an attempted extraction when the file has reached the end, something which the behavior of the stream's boolean operator allows. You should set up an extra try-catch block in f() and rethrow the exception if it doesn't correspond with the end of file condition:
std::ifstream ifs;
std::string word;
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try {
ifs.open(file);
while (ifs >> word) {
if (word == "foo") return true;
}
}
catch (std::ios_base::failure&) {
if (!ifs.eof())
throw;
}
return false;
If the goal is to throw an exception only in case of a problem when opening the file, why not write:
bool f(const char *file)
{
ifstream ifs;
string word;
ifs.open(file);
if (ifs.fail()) // throw only when needed
throw std::exception("Cannot open file !"); // more accurate exception
while (ifs >> word) {
if (word == "foo") return true;
}
return false;
}
You could of course set :
ifs.exceptions(ifstream::badbit);
before or after the the open, to throw an exception in case something really bad would happen during the reading.
basic_ios::operator bool() checks fail(), not !good(). Your loop tries to read one more word after EOF is reached. operator>>(stream&, string&) sets failbit if no characters were extracted. That's why you always exit with an exception.
It's hard to avoid that though. The stream reaches EOF state not when the last character is read, but when an attempt is made to read past the last character. If that happens in the middle of a word, then failbit is not set. If it happens in the beginning (e.g. if the input has trailing whitespace), then failbit is set. You can't really reliably end up in eof() && !fail() state.

fscanf c++ equivalent

I would like to translate the following C code to C++.
FILE *fp = NULL;
fp = fopen("./filename", "r");
int i = 0;
fscanf(fp, "%d\n", &i);
uint16_t j = (uint16_t) i;
This is what I came up with this:
ifstream file;
string filename = "./filename";
file.open(filename.c_str(), ios::in);
errno = 0;
if (file.fail()) {
int tmp = errno;
std::cout << file.c_str () << " not found: strerror(" << tmp << "): " << strerror(tmp) );
}
int i = 0;
file >> i >> std::endl;
uint16_t j = (uint16_t) i;
I would like to know whether the syntax is correct or improvable and more importantly whether it's safe against all kinds of inputs.
int read_int(const std::string file_name) {
std::ifstream file(file_name); //the file will close itself on destruction
std::uint16_t i;
//extract type, don't worry about what it is it will either compile or not
if(!(file >> i)) { //Catch failure
//or however you wish to deal with it.
throw std::runtime_error("can't read file");
}
return i;
}
int main() {
try{
std::uint16_t i=read_int("./filepath");
//do with i...
}
catch(const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Note if you do not have C++11 then you will need to use c_str() to open the file, but the string method is prefered.
EDIT: fstream close themselves, there is no need to close it yourself, the functionality is there incase you do have to do that however it is far better to rely on RAII semantics:
http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
RAII dictates that you should open the file on construction and it will close on destructions, this ensures that there isn't any invalid (exclude EOF, file not found...) fstream object preventing bugs. RAII is a fundamental construct in C++ and should be used where ever resources are concerned.
The docs for the fstream destructors is here:
http://en.cppreference.com/w/cpp/io/basic_fstream
destructs the basic_fstream and the associated buffer, closes the file
The exact equivalent would be:
std::ifstream fs( "./filename" );
int i = 0;
fs >> i >> std::ws;
uint16_t j = i;
Whether this is what you really want is another question: the use of a
"\n" in the format string for fscanf suggests (to me, at least) that
you really want to read a single '\n', and not arbitrary white space;
what the "\n" means in fscanf, however, is to skip up to the next
non-whitespace. (In the case of interactive input, this can be a real
problem, since you won't return from your scanf—or my
replacement above—until you've encountered a non-white space
character or end of file. For input from a file, it may not be an
issue.)
When reading line oriented input, the classical solution is to use
std::getline, and then an std::istringstream to parse it.

C++ Data File with extra empty line

I have data files with extra empty lines in them at the end. This is causing problems with reading in the data lines. I'm using:
while (datFile.good())
but .eof() didn't work either.
Any suggestions?
while (datFile.good())
{
getline (datFile, line);
istringstream liness(line);
int z = 0;
while (z <= index)
{
while (liness >> temp)
{
if (z == 0)
{
values[0].push_back(atof(temp.c_str()));
}
if (z == index)
{
values[1].push_back(atof(temp.c_str()));
}
cout << temp << endl;
z++;
}
}
}
Thou shalt use the stream's status after thy read!
You didn't post enough code to tell but input always looks something like this:
while (in >> data) {
process(data);
}
Whether you use formatted input or unformatted input diesn't matter either. Also, good() is generally not that useful aabd eof() is only useful to suppress an error message: if you read failed it may be OK that it failed because you read all the data. It is never a useful condition to determine whether a read was successful: eof() can yield true although the read was successful.
while (getline (datFile, line))
{
istringstream liness(line);
// the rest of the loop is unchanged.
...
}

Try-Catch Block For C++ File-IO Errors Not Working

I'm very new to the world of C++ error handling, but I was told here:
Checking for file existence in C++
...that the best way to checks for file existence was with a try-catch block. From my limited knowledge on the topic, this sounds like sound advice. I located this snippet of code:
http://www.java2s.com/Tutorial/Cpp/0240__File-Stream/Readafileintrycatchblock.htm
#include <fstream>
#include <iostream>
using namespace std;
int main ()
{
try{
char buffer[256];
ifstream myfile ("test.txt");
while (! myfile.eof() )
{
myfile.getline (buffer,100);
cout << buffer << endl;
}
}catch(...){
cout << "There was an error !\n";
}
return 0;
}
...but when I compile it using
g++ -Wall -pedantic -o test_prog main.cc
And run the program in a directory where test.txt does not exist, the prog keeps spitting out empty lines to the terminal. Can anyone figure out why?
Also is this a good way to check for file existence for a file you actually want to open and read from (versus just something where your indexing a bunch of files and checking them over)?
Thanks!
In C++ iostreams do not throw exeptions by default. What you need is
ifstream myfile("test.txt");
if(myfile) {
// We have one
}
else {
// we dont
}
By default the fstream objects do not throw. You need to use void exceptions ( iostate except ); to set the exception behavior. You can fetch the current settings using iostate exceptions ( ) const;. Change your code just a bit:
#include <fstream>
#include <iostream>
#include <stdexcept>
using namespace std;
int main ()
{
try{
char buffer[256];
ifstream myfile ("test.txt");
myfile.exceptions ( ifstream::eofbit | ifstream::failbit | ifstream::badbit );
while (myfile)
{
myfile.getline (buffer,100);
cout << buffer << endl;
}
myfile.close();
}catch(std::exception const& e){
cout << "There was an error: " << e.what() << endl;
}
return 0;
}
First of all, for the try block to do any good, you need to enable exceptions for the stream.
Second, a loop like:
while (! myfile.eof() )
Will lead to nothing but trouble, and you're seeing that here. The problem (in this case) is that when the file failed to open, eof will never be signaled -- you can't/don't reach the end of the file because there is no file. Therefore, your loop runs forever, on an existentialist search for the end of a nonexistent file. Fix the loop, and things get better in a hurry:
char buffer[256];
ifstream myfile ("test.txt");
while (myfile.getline(buffer,100))
{
cout << buffer << endl;
}
While you're at it, a bit more fixing wouldn't hurt (unless you really meant to use less than half of the space you allocated for your buffer):
char buffer[256];
ifstream myfile ("test.txt");
while (myfile.getline(buffer,sizeof(buffer)))
{
cout << buffer << endl;
}
Or, of course, eliminate the problem entirely:
std::string buffer;
ifstream myfile("test.txt");
while (getline(myfile, buffer))
cout << buffer << "\n";
Edit: note that none of these (at least currently) depends on exceptions at all. They're all set up to write a line to the output if we succeeded in our attempt at reading a line from the input. If the file didn't open, the body of the loop simply won't execute, because we won't be able to read from a file that didn't open. If we want to print an error message telling the user that the file didn't open, we'd have to handle that separately from what's above. For example:
ifstream myfile("test.txt");
if (!myfile) {
std::cerr << "File failed to open";
return FAIL;
}
while (std::getline(myfile // ...