I'm using a FileManager for a project so that reading and writing is less of a hassle for me. Or would be, if I didn't spend all this time debugging it. So, this comfort-class actually caused me stress and time. Awesome.
The problem seems to be the fstream
. Before I continue further, here is the structure of my FileManager class.
class FileManager : Utility::Uncopyable
{
public:
FileManager();
void open(std::string const& filename);
void close();
void read(std::string& buffer);
void write(std::string const& data);
private:
std::fstream stream_;
};
Very simple. The buffer is loaded with data during the read function, the data parameter is what's to be written to file. Before reading and writing you must open the file or risk getting a big, fat exception in your face. Kind of like the one I'm getting now.
Scenario: Simple command-line registering of a user, then writing the data to file. I ask for a name and password. The name is copied and appended with .txt (the filename). So it looks like this:
void SessionManager::writeToFile(std::string const& name,
std::string const& password)
{
std::string filename = name + ".txt";
std::string data;
data += name +", " +password;
try
{
fileManager_->open(filename);
fileManager_->write(data);
fileManager_->close();
}
catch(FileException& exception)
{
/* Clean it up. */
std::cerr << exception.what() << "\n";
throw;
}
}
Problem: the open fails. The file is never created, and during the write I get an exception for not having an open file.
FileManager::open() function:
void FileManager::open(std::string const& filename)
{
if(stream_.is_open())
stream_.close();
stream_.open(filename.c_str());
}
and write
void FileManager::write(std::string const& data)
{
if(stream_.is_open())
stream_ << data;
else
throw FileException("Error. No file opened.\n");
}
However, if I create the file beforehand, then it has no troubles opening the file. Yet, when I check, the default std::ios::openmode
is std::ios::in | std::ios::out
. I can create the file just fine when I only tag std::ios::out
, but I want to keep the stream in a read/write state.
How can I accomplish this?
Best method:
void FileManager::open(std::string const& filename)
{
using std::ios_base;
if( stream_.is_open() )
stream_.close();
stream_.open( filename.c_str() ); // ...try existing file
if( !stream_.is_open() ) // ...else, create new file...
stream_.open(filename.c_str(), ios_base::in | ios_base::out | ios_base::trunc);
}
So the code tests for an existing file and, if not, creates it.
You have to call fstream::open
with an explicit openmode
argument of
ios_base::in | ios_base::out | ios_base::trunc
Otherwise open
will fail due to ENOENT
.
Table 95 of the draft C++ standard lists possible file open modes and their equivalent in stdio
. The default, ios_base::out | ios_base::in
is r+
. The one I listed above is equivalent to w+
. ios_base::out | ios_base::app
is equivalent to a
. All other combinations involving ios_base::app
are invalid.
(At the risk of being scoffed at: you could switch to stdio
and use the file mode a+
, which reads from the start and appends at the end.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With