27

I want to input some contents to a file, but I'd like to check first if a file with the name I wish to create exists. If so, I don't want to create any file, even if the file is empty.

My attempt

bool CreateFile(char name[], char content[]){
     std::ofstream file(name);
     if(file){
         std::cout << "This account already exists" << std::endl;
        return false;
     }
     file << content;
     file.close();
     return true;
}

Is there any way to do what I want?

TBD
  • 509
  • 7
  • 15
Lion King
  • 32,851
  • 25
  • 81
  • 143

9 Answers9

32

Assuming it is OK that the operation is not atomic, you can do:

if (std::ifstream(name))
{
     std::cout << "File already exists" << std::endl;
     return false;
}
std::ofstream file(name);
if (!file)
{
     std::cout << "File could not be created" << std::endl;
     return false;
}
... 

Note that this doesn't work if you run multiple threads trying to create the same file, and certainly will not prevent a second process from "interfering" with the file creation because you have TOCTUI problems. [We first check if the file exists, and then create it - but someone else could have created it in between the check and the creation - if that's critical, you will need to do something else, which isn't portable].

A further problem is if you have permissions such as the file is not readable (so we can't open it for read) but is writeable, it will overwrite the file.

In MOST cases, neither of these things matter, because all you care about is telling someone that "you already have a file like that" (or something like that) in a "best effort" approach.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • 1
    Doesn't (always) work, even without race conditions. If the file has write permission but doesn't have read permissions, your example will overwrite it. – rabensky Jul 23 '13 at 18:38
  • 1
    @MatsPetersson, FYI - this race condition is discussed fairly extensively in "Secure Coding in C and C++" (Robert Seacord) 2nd Edition, Chapter 8, section 5. – Nathan Ernst Jul 23 '13 at 19:05
  • @NathanErnst: It's a fairly well known problem. But it's only a problem when you are either dealing with files that directly have security aspects ("/etc/passwd" or such things), or the application as such has security aspects. For most applications, trying to open for read then opening for write is sufficient, because there is nothing competing for the same filename anyway - it's just a reminder that "you have already used that name, pick another one". – Mats Petersson Jul 23 '13 at 19:11
  • 1
    @MatsPetersson, agreed. Unfortunately, even looking at the C++11 std library, there appears to be no way so solve this problem directly. You can't construct a `basic_filebuf` from a file descriptor, so `fopen` & `fstream` can't work together. Additionally, there are no `openmode` flags available to fail if the file already exists (assuming you also have permissions to the file). Best I can see, is to open in append mode and test `tellp` against 0, and I'm not even sure that would be safe or correct. – Nathan Ernst Jul 23 '13 at 19:20
  • 1
    @NathanErnst: Opening in append mode wouldn't prevent it from using a file that is empty (but exists). But I think the problem here is "support for many OS architectures" - there are OS's that simply do not support opening a file in a way that "works" here (you have to other things to prevent overwriting files - obviously, this pretty much means that it's not a very secure system, but C++ as a language doesn't require security...). – Mats Petersson Jul 23 '13 at 19:23
  • You should also check this: http://stackoverflow.com/questions/12774207/fastest-way-to-check-if-a-file-exist-using-standard-c-c11-c – PapaAtHome Jan 14 '17 at 11:22
13

you can also use Boost.

 boost::filesystem::exists( filename );

it works for files and folders.

And you will have an implementation close to something ready for C++14 in which filesystem should be part of the STL (see here).

Community
  • 1
  • 1
alexbuisson
  • 7,699
  • 3
  • 31
  • 44
8

Try

ifstream my_file("test.txt");
if (my_file)
{
 // do stuff
}

From: How to check if a file exists and is readable in C++?

or you could use boost functions.

Community
  • 1
  • 1
CBIII
  • 845
  • 1
  • 9
  • 9
  • 2
    This might not work if you don't have reading permissions to that file. Of course, then most cases you won't be able to write to it anyway - but there are some systems with weird permissions, and you might have write permissions without read permissions, thus destroying your file. – rabensky Jul 23 '13 at 18:33
7

Try this (copied-ish from Erik Garrison: https://stackoverflow.com/a/3071528/575530)

#include <sys/stat.h>

bool FileExists(char* filename) 
{
    struct stat fileInfo;
    return stat(filename, &fileInfo) == 0;
}

stat returns 0 if the file exists and -1 if not.

Community
  • 1
  • 1
dumbledad
  • 16,305
  • 23
  • 120
  • 273
7

As of C++17 there is:

if (std::filesystem::exists(pathname)) {
   ...
James Hirschorn
  • 7,032
  • 5
  • 45
  • 53
2

Looked around a bit, and the only thing I find is using the open system call. It is the only function I found that allows you to create a file in a way that will fail if it already exists

#include <fcntl.h>
#include <errno.h>

int fd=open(filename, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
if (fd < 0) {
  /* file exists or otherwise uncreatable
     you might want to check errno*/
}else {
  /* File is open to writing */
}

Note that you have to give permissions since you are creating a file.

This also removes any race conditions there might be

vy32
  • 28,461
  • 37
  • 122
  • 246
rabensky
  • 2,864
  • 13
  • 18
  • 2
    Of course, now you have the problem that you can't use the file as an `fstream`, and this only works on a Unix style OS. – Mats Petersson Jul 23 '13 at 19:24
  • 1
    Well, now that you have created it you can always close it and reopen it (you "own" it now - you are sure you created it). As for the unix thing - didn't know that! I have never programed on windows. But google found the `CreateFile` function in msdn, that given the `CREATE_NEW` flag will fail if the file exists. So I guess you could do it with a `#define` for different OSs... There really should be a standard way to do this <. – rabensky Jul 23 '13 at 19:32
0

I just saw this test:

bool getFileExists(const TCHAR *file)
{ 
  return (GetFileAttributes(file) != 0xFFFFFFFF);
}
Racky
  • 1,173
  • 18
  • 24
0

C++17, cross-platform: Using std::filesystem::exists and std::filesystem::is_regular_file.

#include <filesystem> // C++17
#include <fstream>
#include <iostream>
namespace fs = std::filesystem;

bool CreateFile(const fs::path& filePath, const std::string& content)
{
    try
    {
        if (fs::exists(filePath))
        {
            std::cout << filePath << " already exists.";
            return false;
        }
        if (!fs::is_regular_file(filePath))
        {
            std::cout << filePath << " is not a regular file.";
            return false;
        }
    }
    catch (std::exception& e)
    {
        std::cerr << __func__ << ": An error occurred: " << e.what();
        return false;
    }
    std::ofstream file(filePath);
    file << content;
    return true;
}
int main()
{
    if (CreateFile("path/to/the/file.ext", "Content of the file"))
    {
        // Your business logic.
    }
}
Roi Danton
  • 7,933
  • 6
  • 68
  • 80
-1

The easiest way to do this is using ios :: noreplace.

FelixSFD
  • 6,052
  • 10
  • 43
  • 117
Vaibhav
  • 1
  • 1