Stop ofstream from creating a file - c++

I'm trying open a file and check if it exists yet it creates a new file if the given file does not exist. If it is going to automatically create a file, what's the point of the isOpen() then?
int main() {
std::ofstream defaultFile;
defaultFile.open("AAAAA.txt");
std::cout << defaultFile.is_open();//this will always print out "1"
}

what's the point of the isOpen() then?
Creating a file can fail for numerous reasons. One could be that you have no write access in the directory where you try to create the file. In that case
defaultFile.open("AAAAA.txt");
will fail and is_open will return false. If you want to know if the file exists before creating it you can do for example this:
ifstream f(name);
bool exits_and_can_be_opened = f.good();
Taken from here: https://stackoverflow.com/a/12774387/4117728, but note that since C++17 there is the Filesystem library with more straightforward ways to query properties of files.

Related

Correct way to validate the creation of multiple folders and files in C ++

I am working on a personal project, one of the first steps that my program executes is to create some folders and files when the user applies a command through the console.
For example. The user executes the command emi -start through the console, this triggers the creation of the following files and folders:
emi_base
emi_database
emi_main_db_file.csv
emi_catch_db_file.csv
emi_temp_db_file.csv
emi_version
emi_main_version_folder
emi_catch_version_folder
emi_temp_version_folder
emi_config
emi_config_file.txt
emi_ignore_file.txt
emi_repo_file.txt
For this to be possible I use a code similar to this:
// Creating folder emi_base
bool emi_directory_created = fs::create_directory(emi_default_path);
// Creating folder emi_database and its corresponding files
bool db_directory_created = fs::create_directory(db_default_path);
ofstream db_main_file_ostrm(db_main_file, std::ios::out | std::ios::binary);
ofstream db_catch_file_ostrm(db_catch_file, std::ios::out | std::ios::binary);
ofstream db_temp_file_ostrm(db_temp_file, std::ios::out | std::ios::binary);
// Creating folder emi_version and its corresponding sub-folders
bool version_directory_created = fs::create_directory(version_default_path);
bool version_main_directory_created = fs::create_directory(version_main_folder);
bool version_catch_directory_created = fs::create_directory(version_catch_folder);
bool version_temp_directory_created = fs::create_directory(version_temp_folder);
// Creating folder emi_config and its corresponding files
bool config_directory_created = fs::create_directory(config_default_path);
if (emi_directory_created &&
db_directory_created &&
db_main_file_ostrm &&
db_catch_file_ostrm &&
db_temp_file_ostrm &&
version_directory_created &&
version_main_directory_created &&
version_catch_directory_created &&
version_temp_directory_created &&
config_directory_created) {
// Code that is executed after validating that all files and folders were created correctly.
}
Well, for now everything works correctly, however, I do not know if that is the best way to validate that the folders and the respective files have been created correctly, it seems to me that putting all those Booleans in a conditional is not the right thing to do.
Is there a better way to do this? Is there a better way to validate the creation of multiple folders and files at once or in sequence?
Hope someone can give this newbie some feedback :)
If fs::create_directory is std::filesystem::create_directory, then fs::create_directory(<path>) throws an exception if the directory cannot be created, you don't need to check its return value:
The overload that does not take a std::error_code& parameter throws filesystem_error on underlying OS API errors, constructed with p as the first path argument and the OS error code as the error code argument.
But you do need to check that the files were opened successfully with ofstream::is_open call. You may like to create a helper function that opens an ofstream and checks whether that succeeded and then return ofstream, in C++11 ofstream is moveable and hence can be returned by value. E.g.:
std::ofstream open_ofstream(char const* filename) {
std::ofstream f(filename, std::ios::out | std::ios::binary);
if(!f.is_open())
throw std::system_error{errno, std::system_category(), filename}; // Hoping that std::ofstream didn't mangle errno.
// or throw std::filesystem::filesystem_error{"failed to open", filename, std::error_code{errno, std::system_category()}};
return f;
}
If all your directories and files reside in one top-level directory, you may like to create it with a different name first, create the directory/file structure in it and initialize the files, and then rename the top-level directory to its final name. So that when the top-level directory exists that means that it is correctly initialized and is ready for use.
I think better way is throw an exception in create_directory funciton if folder creation fail. So you dont need to check every create_directory call. Just one try-catch will be enough.

How to generate a file open failure for unit testing

For completeness, I'm using C++ in Visual Studio 2012 under Windows 7.
I have some basic file reading code:
Defined in class:
std::ifstream iniFileHandle;
Method in question:
FileLoader::FILE_STATUS FileLoader::loadFile(const std::string& fullpathandfilename)
{
if (fullpathandfilename.empty())
{
iniFileStatus = FILE_NOT_SPECIFIED;
}
else // parameter has 'something'...
{
if (!doesFileExist(fullpathandfilename))
{
iniFileStatus = FILE_NOT_FOUND;
}
else // file found...
{
iniFileHandle.open(fullpathandfilename, std::ios::in);
if (!iniFileHandle.is_open()) // something went wrong
{
iniFileStatus = FILE_CANNOT_LOAD; // <== HOW TO TEST?
}
else // file opened!
{
iniFileStatus = FILE_OPEN;
}
}
}
return iniFileStatus;
}
All this works fine but I'm having difficulty getting coverage with Google Unit Tests.
What i'm missing is a way to simulate/create a scenario where is_open() fails.
I've tried:
read only files
the 'exe' being currently run
a file opened in another application
but they all open.
Is this error condition actually possible to occur? If so, how can I mimic the failure so I can test it?
As mentioned in the comments, trying to open a directory will cause is_open() to return false. The cases you tried all succeed because you open the file as read-only (std::ios::in, which is also the default for an ifstream).
I've tried:
a file opened in another application but they all open.
This happens because file is opened in share mode which allows reading for others. You need to open file in exclusive file access mode. To achieve it, if you use MSVS, you could pass the third parameter int _Prot to the ifstream, otherwise you could use CreateFile().
To emulate the problem you could open file exclusively used by Windows, e.g:
c:\pagefile.sys
create a any file, and set its permission as read only, and in your code try to open this file for writing

Data isn't being saved in the text file (C++ Fstream library )

I've been trying to save the score of the player in the game in a text file, but it doesn't do so.
This is the code I'm using:
//some code above
std::fstream TextScore ("Ranking.txt");
// some code above
if (Player->getFinal(Map) == true)
{
TextScore.open("Ranking.txt", ios::out);
TextScore << Player->getPoints();
TextScore.close();
//some code below
}
Then I check the text file and nothing has been saved, the file is empty.
I would like to know what I'm missing or doing wrong.
Thanks in advance.
std::fstream TextScore ("Ranking.txt");
This opens the file, as if TextScore.open("Ranking.txt"), std::ios::in|std::ios::out) was called.
TextScore.open("Ranking.txt", std::ios::out);
This opens it again.
The combination is not going to work if the file already exists. The first open will succeed and the second one will fail. After that, all I/O operations will fail. Open it just once, either in the constructor or in a separate open call. The most idiomatic C++ way would be
{
std::fstream TextScore ("Ranking.txt", std::ios::out);
TextScore << Player->getPoints();
}
No need to close the file explicitly thanks to RAII.
Opening the same file twice is certainly going to cause problems. Move the definition of TextScore into the body of the if statement in place of the call to TextScore.open(). And then you can remove the call to TextScore.close(); the destructor will close the file.

ifstream: how to tell if specified file doesn't exist

I want to open a file for reading. However, in the context of this program, it's OK if the file doesn't exist, I just move on. I want to be able to identify when the error is "file not found" and when the error is otherwise. Otherwise means I need to quit and error.
I don't see an obvious way to do this with fstream.
I can do this with C's open() and perror(). I presumed that there was a fstream way to do this as well.
EDIT: I've been notified that this does not necessarily indicate a file does not exist, as it may be flagged due to access permissions or other issues as well.
I know I'm extremely late in answering this, but I figured I'd leave a comment anyway for anyone browsing. You can use ifstream's fail indicator to tell if a file exists.
ifstream myFile("filename.txt");
if(myFile.fail()){
//File does not exist code here
}
//otherwise, file exists
I don't think you can know if "the file doesn't exist". You could use is_open() for generic checking:
ofstream file(....);
if(!file.is_open())
{
// error! maybe the file doesn't exist.
}
If you are using boost you could use boost::filesystem:
#include <boost/filesystem.hpp>
int main()
{
boost::filesystem::path myfile("test.dat");
if( !boost::filesystem::exists(myfile) )
{
// what do you want to do if the file doesn't exist
}
}
Since the result of opening a file is OS-specific, I don't think standard C++ has any way to differentiate the various types of errors. The file either opens or it doesn't.
You can try opening the file for reading, and if it doesn't open (ifstream::is_open() returns false), you know it either doesn't exist or some other error happened. Then again, if you try to open it for writing afterwards and it fails, that might fall under the "something else" category.
A simple way from http://www.cplusplus.com/forum/general/1796/
ifstream ifile(filename);
if (ifile) {
// The file exists, and is open for input
}
You can use stat, which should be portable across platforms and is in the standard C library:
#include <sys/stat.h>
bool FileExists(string filename) {
struct stat fileInfo;
return stat(filename.c_str(), &fileInfo) == 0;
}
If stat returns 0, the file (or directory) exists, otherwise it doesn't. I assume that you'll have to have access permissions on all directories in the file's path. I haven't tested portability, but this page suggests it shouldn't be an issue.
A better way:
std::ifstream stream;
stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
stream.open(fileName, std::ios::binary);
With C++17 you can use std::filesystem::exists.
Let's me give example with real running:
file does't exist:
file exist:
see http://www.cplusplus.com/reference/fstream/ifstream/ for more information about its public function.
Straight way without creating ifstream object.
if (!std::ifstream(filename))
{
// error! file doesn't exist.
}

How to create a temporary text file in C++?

I'm trying to create a temporary text file in C++ and then delete it at the end
of the program. I haven't had much luck with Google.
Could you tell me which functions to use?
The answers below tell me how to create a temp file. What if I just want to
create a file (tmp.txt) and then delete it? How would I do that?
Here's a complete example:
#include <unistd.h>
int main(void) {
char filename[] = "/tmp/mytemp.XXXXXX"; // template for our file.
int fd = mkstemp(filename); // Creates and opens a new temp file r/w.
// Xs are replaced with a unique number.
if (fd == -1) return 1; // Check we managed to open the file.
write(fd, "abc", 4); // note 4 bytes total: abc terminating '\0'
/* ...
do whatever else you want.
... */
close(fd);
unlink(filename); // Delete the temporary file.
}
If you know the name of the file you want to create (and are sure it won't already exist) then you can obviously just use open to open the file.
tmpnam and tmpfile should probably be avoided as they can suffer from race conditions - see man tmpfile(3) for the details.
Maybe this will help
FILE * tmpfile ( void );
http://www.cplusplus.com/reference/clibrary/cstdio/tmpfile/
Open a temporary file
Creates a temporary binary file, open
for update (wb+ mode -- see fopen for
details). The filename is guaranteed
to be different from any other
existing file. The temporary file
created is automatically deleted when
the stream is closed (fclose) or when
the program terminates normally.
See also
char * tmpnam ( char * str );
Generate temporary filename
A string containing a filename
different from any existing file is
generated. This string can be used to
create a temporary file without
overwriting any other existing file.
http://www.cplusplus.com/reference/clibrary/cstdio/tmpnam/
This may be a little off-topic because the author wanted to create a tmp.txt and delete it after using it, but that is trivial - you can simple open() it and delete it (using boost::filesystem of course).
mkstemp() is UNIX-based. With Windows you use GetTempFileName() and GetTempPath() to generate a path to a temp file. Sample code from MSDN:
http://msdn.microsoft.com/en-us/library/aa363875%28VS.85%29.aspx
On Linux (starting with kernel 3.11), there's flag to open(2) O_TMPFILE that creates a temporary file that doesn't have a name (i.e. it doesn't show up in the filesystem). This has a few interesting features:
No worries about unique names, it's just an inode, there is no name.
No race conditions during creation (e.g. symlink attacks).
No stray files if your app crashes, it's always automatically deleted.
I wonder why most of you guys showed him the C way of doing it instead of the C++ way.
Here's fstream.
Try that, deleting a file is OS depended but you can use boost.filesystem to make things easy for you.
If you need a named file (for example, so you can pass the name to another process, perhaps a compiler or editor), then register a cleanup function that removes the file with atexit(). You can use either C++ <iostream> or C FILE * (<cstdio>) to create the file. The not completely standard but widely available mkstemp() function creates a file and tells you its name as well as returning a file descriptor (a third I/O mechanism); you could use the fdopen() function to convert the file descriptor into a FILE *.
If you don't need a named file a C-style FILE * is OK, then look at tmpfile() as suggested by #Tom.
A clean, portable and non-deprecated way of creating a temporary file is provided by Boost:
auto temporary_file = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
Well, assuming you have been successful in creating the temporary file, you can use the remove function to delete it.
The function is declared in stdio.h -
#include <stdio.h>
int remove(const char *pathname);
For example, if you want to delete a file named myfile.txt the code will be
#include<stdio.h>
int main()
{
if(remove("myfile.txt") == -1)
{
fprintf(stderr,"Remove failed");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
I hope by now, you already know how to create the temp file, so this should resolve your query.
Hope it helps.