Is it possible use getline() to read a valid file without setting failbit? I would like to use failbit so that an exception is generated if the input file is not readable.
The following code always outputs basic_ios::clear as the last line - even if a valid input is specified.
test.cc:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main(int argc, char* argv[])
{
ifstream inf;
string line;
inf.exceptions(ifstream::failbit);
try {
inf.open(argv[1]);
while(getline(inf,line))
cout << line << endl;
inf.close();
} catch(ifstream::failure e) {
cout << e.what() << endl;
}
}
input.txt:
the first line
the second line
the last line
results:
$ ./a.out input.txt
the first line
the second line
the last line
basic_ios::clear
You can't. The standard says about getline:
If the function extracts no characters, it calls is.setstate(ios_base::failbit) which may throw ios_base::failure (27.5.5.4).
If your file ends with an empty line, i.e. last character is '\n', then the last call to getline reads no characters and fails. Indeed, how did you want the loop to terminate if it would not set failbit? The condition of the while would always be true and it would run forever.
I think that you misunderstand what failbit means. It does not mean that the file cannot be read. It is rather used as a flag that the last operation succeeded. To indicate a low-level failure the badbit is used, but it has little use for standard file streams. failbit and eofbit usually should not be interpreted as exceptional situations. badbit on the other hand should, and I would argue that fstream::open should have set badbit instead of failbit.
Anyway, the above code should be written as:
try {
ifstream inf(argv[1]);
if(!inf) throw SomeError("Cannot open file", argv[1]);
string line;
while(getline(inf,line))
cout << line << endl;
inf.close();
} catch(const std::exception& e) {
cout << e.what() << endl;
}
Related
This minimal program uses boost::program_options to parse a stringstream. Strangely, after parsing, the stream is not in a "good" state anymore and both failbit and eofbit are set.
#include <iostream>
#include <sstream>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
void test_stream(std::stringstream& s);
int main()
{
using namespace std;
namespace po = boost::program_options;
stringstream s;
s << "seed=3" << '\n';
test_stream(s);
po::options_description desc("");
desc.add_options()
("seed", po::value<int>());
po::variables_map vm;
po::store(po::parse_config_file(s, desc, true), vm);
po::notify(vm);
test_stream(s);
return 0;
}
void test_stream(std::stringstream& s)
{
using namespace std;
if (s.good())
{
cout << "stream is good" << endl;
}
else
{
cout << "stream is not good" << endl;
if (s.rdstate() & ios_base::badbit)
cout << "badbit is set" << endl;
if (s.rdstate() & ios_base::failbit)
cout << "failbit is set" << endl;
if (s.rdstate() & ios_base::eofbit)
cout << "eofbit is set" << endl;
}
}
Output:
stream is good
stream is not good
failbit is set
eofbit is set
Although the eof condition is somehow expected, since presumably the parser has read the stream until EOF, why is also the failbit set?
According to the documentation of the ios::eof flag this might happen in certain cases:
Operations that attempt to read at the End-of-File fail, and thus both the eofbit and the failbit end up set. This function can be used to check whether the failure is due to reaching the End-of-File or to some other reason.
Boost's parser uses std::copy() and an iterator on the stream to extract the options. As pointed out in Jerry Coffin's answer to another question, the iterator reads the items from the sequence. After reading the last one the sequence's eof bit is set. When the iterator is incremented another time to get the end-of-stream iterator, which is the condition to leave the loop in copy(), it tries to read again and therefor also the stream's fail bit is set.
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.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
//reading the text file
ifstream inputFile("testfile1.txt");
inputFile.open("testfile1.txt");
while(!inputFile.eof())
//eof till end of file, reads the txt till end of file
{
string str;
getline(inputFile,str);
cout <<str<< endl;
}
inputFile.close();
return 0;
}
// The problem that i am having is that it doesn not read the file or anything in it. Doing nothing it says Program ended with exit code: 0. Could anyone check the mistake in the code
First Bug: You are opening the input file twice. Per the C++ standard, regarding the behavior of your second open request (the direct call to the open member):
C++11 § 27.9.1.9 [ifstream.members/3]
void open(const char* s, ios_base::openmode mode = ios_base::in);
Effects: Calls rdbuf()->open(s, mode | ios_base::in). If that function
does not return a null pointer calls clear(), otherwise calls
setstate(failbit) (which may throw ios_base::failure (27.5.5.4)).
which therefore asks the question, what does rdbuf()->open(...) do ? Well, a std::ifstream uses a filebuf for it's buffering, and once again, per the standard:
C++11 §27.9.1.4 [filebuf.members/2]
basic_filebuf<charT,traits>* open(const char* s, ios_base::openmode mode);
Effects: If is_open() != false, returns a null pointer. Otherwise, initializes the filebuf as required. ...
In short, your double-open is putting your stream into a fail-state, which means all data-related operations with it are going to fail outright from that point on.
Second Bug: Improper use of .eof in a loop conditional expression. you'll run into this once you fix the first bug. The reasons this is not being done correctly are explained in the following question far better than I can explain it here.
Why is iostream::eof inside a loop condition considered wrong?
Suffice it to say, check your IO operations, not just the eof-state of the stream. Get into that habit and stick with it.
Fixing both, your code can literally be reduced to simply this:
#include <iostream>
#include <fstream>
#include <string>
int main()
{
std::ifstream inputFile("testfile1.txt");
std::string str;
while (std::getline(inputFile, str))
std::cout << str << std::endl;
}
Obviously if you're shooting for more robust code, you probably want to perform some error handling in there, something like:
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
int main()
{
std::ifstream inputFile("testfile1.txt");
if (!inputFile)
{
std::cerr << "Failed to open file\n";
return EXIT_FAILURE;
}
std::string str;
while (std::getline(inputFile, str))
std::cout << str << std::endl;
}
This is the correct way to read a file according to this article!
The problem in your code it seems that you are using an IDE and it cannot find the path you are giving to ifstream so try to give a full path to the file. Hope it can help u.
string line;
ifstream f("/YOUPARTH/testfile1.txt");
if (!f.is_open())
perror("error while opening file");
while(getline(f, line)) {
cout << line << endl;
}
if (f.bad())
perror("error while reading file");
return 0;
Translate the while statement: "While inputFile is at End Of File" .. you want the negation of that.
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.
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 // ...