catch an open exception - c++

I have this code and just wondered why it's not throwing an exception (or in which case it should do).
From cplusplus.com :
If the function fails to open a file, the failbit state flag is set for the stream (which may throw ios_base::failure if that state flag was registered using member exceptions).
#include <fstream>
#include <exception>
#include <iostream>
int main() {
std::ofstream fs;
try {
fs.open("/non-existing-root-file");
} catch (const std::ios_base::failure& e) {
std::cout << e.what() << std::endl;
}
if (fs.is_open())
std::cout << "is open" << std::endl;
else
std::cout << "is not open" << std::endl;
return 0;
}

You did not follow the rabbit trail all of the way down
You need to tell it you want exceptions by using std::ios::exceptions. Without doing this, it indicates failure through the failbit state flag.
// ios::exceptions
#include <iostream> // std::cerr
#include <fstream> // std::ifstream
int main () {
std::ifstream file;
///next line tells the ifstream to throw on fail
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;
}

You would need to register the failbit flag using std::ios::exceptions() for it to throw, which you haven't done.

I dont think fstream throws an exception on failure to open file. you have to check that with bool fstream::is_open().

Related

std::ofstream: can open file but not append

My program opens and writes to several files incrementally but at some seemingly arbitrary point appending text to an already existing file fails. Here's the offending piece of code:
bool append_to_file(std::string const &Path, std::string const &What) {
std::ofstream FStream(Path, std::ios::out | std::ios::app);
if (!FStream) {
std::cerr << "OPEN FAILED" << std::endl;
return false;
}
FStream << What;
if (!FStream) {
std::cerr << "WRITE FAILED" << std::endl;
return false;
}
return true;
}
When this function fails, the first check succeeds but the second doesn't and prints:
WRITE FAILED. The file in question definitely does exist in the filesystem both before and after the function is called and has read and write permissions set. I can't make sense of this, why would opening the file succeed but appending fail in this scenario?
EDIT: this is not reproducible given the information I've provided and should be closed.
This works for me:
#include <string>
#include <fstream>
#include <iostream>
int append_to_file(std::string const& Path, std::string const& What) {
std::fstream FStream(Path,std::ios_base::app);
if (!FStream) {
std::cerr << "OPEN FAILED" << std::endl;
return 1;
}
FStream << What;
if (!FStream) {
std::cerr << "WRITE FAILED" << std::endl;
return 2;
}
return 0;
}
int main() {
int res = append_to_file("Tester.txt", "HIIII");
std::cout << res;
}
In the example you provided, had you #included <iostream>, <string> and <fstream>? Also, when opening with std::ios_base::app, you don't have to have to open with std::ios_base::out. Also, I changed the return true; and return false; so that it returns a number depending on where it failed. This makes it easier to trace the error. Finally, I changed std::ofstream to std::fstream, because we are opening customly. After that, it worked perfectly.

How to catch I/O exception (exactly I/O, not std::exception)

I tried the example program from here (with mingw-w64). The program crashed. So I edited it:
#include <iostream> // std::cerr
#include <fstream> // std::ifstream
int main()
{
std::ifstream file;
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try {
file.open("not_existing.txt");
while (!file.eof())
file.get();
file.close();
}
catch (std::ifstream::failure e) {
std::cerr << "Exception opening/reading/closing file\n";
}
catch (const std::exception& e) {
std::cerr << "should not reach this";
}
return 0;
}
Now it runs, but prints should not reach this, while I was expecting it to print Exception opening/reading/closing file.
Why is my expectation wrong?
EDIT:
since this seems to be an important point, here's the exact version om my compiler: mingw-w64 version "x86_64-6.2.0-posix-sjlj-rt_v5-rev1" , i.e. GCC version 6.2
This may be a MingW bug. I get the expected result using MacOS Clang 802.0.42. The expected output being:
Exception opening/reading/closing file
This might be a known regression: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66145

R6010 abort() has been called

I read about substr from here
http://www.cplusplus.com/reference/string/string/substr/
Here is my code :
int main()
{
std::ifstream in ("c:\\users\\admin\\desktop\\aaa.txt");
std::ofstream out ("c:\\users\\admin\\desktop\\bbb.txt");
std::string s ;
while ( getline (in,s) )
{
std::size_t startpos = s.find("test");
std::string str = s.substr (startpos);
out << str << endl;
}
in.close();
out.close();
}
I get error : R6010 abort() has been called
Note : aaa.txt contains spaces/characters/html tags
Any idea ?
Since I dont know the content of the text file, could you try making the following changes and let me know if the error is still being shown:
#include <fstream>
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
ifstream in("example.txt");
ofstream out("bbb.txt");
string s = std::string();
string str = std::string();
while (getline(in, s))
{
size_t startpos = s.find("test");
cout << s;
if (startpos != std::string::npos){
str = s.substr(startpos);
out << str << endl;
}
}
in.close();
out.close();
getchar();
return 0;
}
I am using if (startpos != std::string::npos) condition to check what to do when the find succeeds, this is missing in your code. adding this case will resolve your error.
Keep coding :)
While Code Frenzy answer is right, you can also use exceptions to help catch these kind of errors:
#include <fstream>
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
std::ifstream in ("aaa.txt");
std::ofstream out ("bbb.txt");
std::string s ;
try
{
while ( getline (in,s) )
{
std::size_t startpos = s.find("test");
std::string str = s.substr (startpos);
out << str << endl;
}
in.close();
out.close();
}
catch(std::exception e)
{
// (1) it will catch the error show show
cerr << e.what() << endl;
}
catch(std::out_of_range e)
{
// (2) this will also catch the same error if (1) was not there but could
// get you more details if you wanted since its more specific but i have
// not digged into it further
cerr << e.what() << endl;
}
catch(...)
{
// (3) just for sanity check if first two didn't catch it
cerr << "something went wrong";
}
}
The exceptoin catches this error and prints the message:
invalid string position

File not opening with C++ fstream even with full path

string mapFile;
cout << "Enter the file name : ";
cin >> mapFile;
ifstream mapfh;
mapfh.open(mapFile.c_str());
if(mapfh.is_open()) { ... }
else //if board file did not open properly
{
throw;
}
mapfh.close();
I am compiling with g++ in the command line. Whenever I put a file input (even with a full path i.e. /User/...etc./file.txt) it throws an error. I know the input is good, but for whatever reason the open always fails.
This isn't fully portable, but you'll get a more informed output if you interpret the errno,
#include <cerrno>
#include <cstring>
...
if(mapfh.is_open()) { ... }
else //if board file did not open properly
{
std::cout << "error: " << strerror(errno) << std::endl;
throw;
}
And if your policy is to communicate the errors as exceptions then use iostreams native support for the exceptions:
ifstream mapfh;
mapfh.exceptions(std::ios::failbit);
try {
mapfh.open(mapFile.c_str());
...
mapfh.close();
} catch (const std::exception& e) {
std::cout << e.what() << " : " << std::strerror(errno) << std::endl;
}

std::ifstream setting fail() even when no error

Using GCC 4.7.3 on Cygwin 1.7.24. Compiler options include: -std=gnu++11 -Wall -Wextra
I am working on a command line application and I needed to be able to load and save a set of strings so I wrote a quick wrapper class around std::set to add load and save methods.
// KeySet.h
#ifndef KEYSET_H
#define KEYSET_H
#include <cstdlib>
#include <sys/stat.h>
#include <cerrno>
#include <cstring>
#include <string>
#include <set>
#include <iostream>
#include <fstream>
inline bool file_exists (const std::string& filename)
{
/*
Utility routine to check existance of a file. Returns true or false,
prints an error and exits with status 2 on an error.
*/
struct stat buffer;
int error = stat(filename.c_str(), &buffer);
if (error == 0) return true;
if (errno == ENOENT) return false;
std::cerr << "Error while checking for '" << filename << "': " << strerror(errno) << std::endl;
exit (2);
}
class KeySet
{
private:
std::string filename;
std::set<std::string> keys;
public:
KeySet() {}
KeySet(const std::string Pfilename) : filename(Pfilename) {}
void set_filename (const std::string Pfilename) {filename = Pfilename;}
std::string get_filename () {return filename;}
auto size () -> decltype(keys.size()) {return keys.size();}
auto cbegin() -> decltype(keys.cbegin()) {return keys.cbegin();}
auto cend() -> decltype(keys.cend()) {return keys.cend();}
auto insert(const std::string key) -> decltype(keys.insert(key)) {return keys.insert(key);}
void load ();
void save ();
};
void KeySet::load ()
{
if (file_exists(filename)) {
errno = 0;
std::ifstream in (filename, std::ios_base::in);
if (in.fail()) {
std::cerr << "Error opening '" << filename << "' for reading: " << strerror(errno) << std::endl;
exit (2);
}
std::string token;
if (token.capacity() < 32) token.reserve(32);
while (in >> token) keys.insert(token);
if (!in.eof()) {
std::cerr << "Error reading '" << filename << "': " << strerror(errno) << std::endl;
exit (2);
}
in.clear(); // need to clear flags before calling close
in.close();
if (in.fail()) {
std::cerr << "Error closing '" << filename << "': " << strerror(errno) << std::endl;
exit (2);
}
}
}
void KeySet::save ()
{
errno = 0;
std::ofstream out (filename, std::ios_base::out);
if (out.fail()) {
std::cerr << "Error opening '" << filename << "' for writing: " << strerror(errno) << std::endl;
exit (2);
}
for (auto key = keys.cbegin(), end = keys.cend(); key != end; ++key) {
out << *key << std::endl;
}
out.close();
if (out.fail()) {
std::cerr << "Error writing '" << filename << "': " << strerror(errno) << std::endl;
exit (2);
}
}
#endif
//
Here's a quick program to test the load method.
// ks_test.cpp
#include "KeySet.h"
int main()
{
KeySet test;
std::string filename = "foo.keys.txt";
test.set_filename(filename);
test.load();
for (auto key = test.cbegin(), end = test.cend(); key != end; ++key) {
std::cout << *key << std::endl;
}
}
The data file just has "one two three" in it.
When I go to run the test program, I get the following error from my test program:
$ ./ks_test
Error closing 'foo.keys.txt': No error
Both cppreference.com and cplusplus.com say that the close method should set the fail bit on error. The save method works fine, and the load method works correctly if I comment out the error check after the close. Should this really work or have I misunderstood how close is supposed to work? Thanks in advance.
Edited to clarify, fix typo's and adjust code per Joachim Pileborg's and Konrad Rudolph's comments.
Edited to add solution to the code.
You have two errors here: The first is about how you do your reading, more specifically the loop for reading. The eof flag will not be set until after you tried to read and the read failed. Instead you should do like this:
while (in >> token) { ... }
Otherwise you will loop one time to many and try to read beyond the end of the file.
The second problem is the one you notice, and it depends on the the first problem. Since you try to read beyond the end of the file, the stream will set failbit causing in.fail() to return true even though there is no real error.
As it turns out, the close method for ifstream (and I assume all other IO objects) DOES NOT clear the error flags before closing the file. This means you need to add an explicit clear() call before you close the stream after end of file if you are checking for errors during the close. In my case, I added in.clear(); just before the in.close(); call and it is working as I expect.