如何在创建新文件之前检查文件是否存在

27
我想把一些内容输入到文件中,但我想先检查一下是否已经存在一个同名的文件。如果是这样,即使该文件为空,我也不想创建任何文件。
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;
}

有没有办法实现我想要的?


4
您先尝试打开并阅读它,好吗? - Carl Norum
1
第二种方法是检查路径是否存在,可以使用fstat()函数。 - Grijesh Chauhan
1
我知道Windows允许您在CreateFile中将其处理为原子操作。我希望其他常见的系统也有相应的行为。C++标准库中肯定有可用的抽象吧? - David Heffernan
你也应该查看这个链接:https://dev59.com/XGcs5IYBdhLWcg3wh0f7 - PapaAtHome
9个回答

32
假设不需要原子操作,您可以这样做:

假设不需要原子操作,您可以这样做:

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;
}
... 

请注意,如果您运行多个线程尝试创建相同的文件,则此方法无效,并且肯定不能防止第二个进程“干扰”文件创建,因为您有TOCTUI问题。[我们首先检查文件是否存在,然后创建它-但是在检查和创建之间,其他人可能已经创建了它-如果这很关键,您需要做一些其他事情,这不是可移植的]。
另一个问题是,如果您拥有诸如文件不可读(因此我们无法打开它以进行读取)但可写的权限,则会覆盖该文件。
在大多数情况下,这两件事都不重要,因为您只关心告诉某人“您已经有了这样的文件”(或类似内容),采用“最佳努力”方法。

1
即使没有竞态条件,也不总是有效。如果文件具有写入权限但没有读取权限,则您的示例将覆盖它。 - rabensky
1
@MatsPetersson,FYI - 这个竞争条件在《C和C++安全编码》(Robert Seacord)第二版第8章第5节中有详细讨论。 - Nathan Ernst
@NathanErnst:这是一个相当知名的问题。但只有在你处理直接涉及安全方面的文件(如“/etc/passwd”等)或应用程序本身具有安全方面时才会出现问题。对于大多数应用程序来说,尝试打开读取然后打开写入就足够了,因为没有任何竞争同一文件名 - 这只是一个提醒,“你已经使用了那个名称,请选择另一个”。 - Mats Petersson
1
@MatsPetersson,同意。不幸的是,即使查看C++11 std库,似乎也没有直接解决此问题的方法。您无法从文件描述符构造basic_filebuf,因此fopenfstream无法一起使用。此外,如果文件已经存在(假设您也有文件权限),则没有可用的openmode标志可以失败。我所能看到的最好的方法是以追加模式打开并测试tellp是否为0,但我甚至不确定这是否安全或正确。 - Nathan Ernst
1
@NathanErnst:以追加模式打开文件并不会阻止它使用一个空的(但存在的)文件。但我认为这里的问题是“支持许多操作系统架构”——有些操作系统根本不支持以一种“有效”的方式打开文件(你必须采取其他措施来防止覆盖文件——显然,这基本上意味着它不是一个非常安全的系统,但C++作为一种语言并不要求安全性……)。 - Mats Petersson
你还应该查看这个链接:https://dev59.com/XGcs5IYBdhLWcg3wh0f7 - PapaAtHome

13

你也可以使用Boost。

 boost::filesystem::exists( filename );

它适用于文件和文件夹。

这样,您将拥有一个实现接近于C++14的版本,其中文件系统应该是STL的一部分(请参见这里)。


目前最佳答案,+1 是因为它跨平台。 - Yonatan Simson

8

2
如果您没有对该文件的读取权限,这可能无法正常工作。当然,在大多数情况下,您也无法对其进行写入 - 但是有些系统具有奇怪的权限,您可能具有写入权限而没有读取权限,从而破坏您的文件。 - rabensky

7
尝试以下内容(从Erik Garrison那里复制而来:https://dev59.com/OXI-5IYBdhLWcg3w1sXi#3071528
#include <sys/stat.h>

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

stat命令返回0表示文件存在,返回-1表示文件不存在。


这只适用于Linux(POSIX)吗?它在标准的MinGW安装中也能工作吗? - Youda008

6

从C++17开始,有以下内容:

if (std::filesystem::exists(pathname)) {
   ...

3
只用了19年... xD - kayleeFrye_onDeck
使用 msvc 14 的语法如下:#include <filesystem> ... return std::experimental::filesystem::exists(filename); - user2410689

2

我稍微找了一下,发现唯一的方法就是使用open系统调用。这是我找到的唯一一个允许你以方式创建文件的函数,如果文件已经存在,则会失败。

#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 */
}

请注意,由于您正在创建文件,因此必须授予权限。

这也消除了可能存在的任何竞争条件。


2
当然,现在你有一个问题,就是无法将该文件用作“fstream”,而且这仅适用于类Unix操作系统。 - Mats Petersson
1
现在你已经创建了它,你可以随时关闭并重新打开它(你现在“拥有”它——你确定你创建了它)。至于Unix的事情——不知道!我从来没有在Windows上编程。但是谷歌在msdn中找到了CreateFile函数,如果给定CREATE_NEW标志,则会失败如果文件存在。所以我猜你可以用不同操作系统的#define来实现...真的应该有一种标准方法来做这个<.< - rabensky

0

C++17,跨平台:使用 std::filesystem::existsstd::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.
    }
}

0

我刚刚看到了这个测试:

bool getFileExists(const TCHAR *file)
{ 
  return (GetFileAttributes(file) != 0xFFFFFFFF);
}

-1

最简单的方法是使用ios::noreplace


没有这样的东西。 - BarbaraKwarc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接