【问题标题】:How to check if a file exists before creating a new file如何在创建新文件之前检查文件是否存在
【发布时间】:2013-07-22 23:45:13
【问题描述】:

我想在文件中输入一些内容,但我想先检查是否存在具有我希望创建的名称的文件。如果是这样,我不想创建任何文件,即使文件是空的。

我的尝试

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

有什么方法可以做我想做的事吗?

【问题讨论】:

  • ...先尝试打开阅读?
  • 第二种方法是检查路径是否存在fstat()
  • 我知道 Windows 允许您将其作为 CreateFile 中的原子操作来处理。我希望其他常见系统具有相同的行为。 C++ 标准库中肯定有可用的抽象吗?
  • 你也应该检查这个:stackoverflow.com/questions/12774207/…

标签: c++ file


【解决方案1】:

环顾了一下,我发现唯一的事情是使用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 */
}

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

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

【讨论】:

  • 当然,现在你的问题是不能将文件用作fstream,而且这只适用于Unix风格的操作系统。
  • 好吧,既然你已经创建了它,你可以随时关闭它并重新打开它(你现在“拥有”它 - 你确定你创建了它)。至于 unix 的东西 - 不知道!我从来没有在windows上编程过。但是 google 在 msdn 中找到了 CreateFile 函数,如果文件存在,则给定 CREATE_NEW 标志将失败。所以我想你可以用#define 来为不同的操作系统做到这一点......真的应该有一个标准的方法来做到这一点<.>
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    从 C++17 开始有:

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

    【讨论】:

    • 只用了 19 年...xD
    • 使用 msvc 14 的语法是:#include ... return std::experimental::filesystem::exists(filename);
    【解决方案4】:

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

    【讨论】:

    • 没有这样的东西。
    【解决方案5】:

    我刚刚看到了这个测试:

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

    【讨论】:

      【解决方案6】:

      您也可以使用 Boost。

       boost::filesystem::exists( filename );
      

      它适用于文件和文件夹。

      您将获得一个接近 C++14 的实现,其中文件系统应该是 STL 的一部分(请参阅here)。

      【讨论】:

      • 迄今为止的最佳答案 +1 跨平台
      【解决方案7】:

      试试这个(从 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 如果文件存在则返回0,否则返回-1

      【讨论】:

      • 这不只适用于 Linux (POSIX) 吗?它是否也适用于标准 MinGW 安装?
      【解决方案8】:

      假设操作不是原子的就可以了,你可以这样做:

      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 问题。 [我们首先检查文件是否存在,然后创建它——但其他人可能在检查和创建之间创建了它——如果这很关键,你将需要做一些其他不可移植的事情]。

      另一个问题是,如果您有权限,例如文件不可读(因此我们无法打开它进行读取)但可写,它将覆盖文件。

      在大多数情况下,这些事情都不重要,因为您所关心的只是以“尽力而为”的方式告诉某人“您已经拥有类似的文件”(或类似的文件)。

      【讨论】:

      • 不(总是)工作,即使没有竞争条件。如果文件有写权限但没有读权限,您的示例将覆盖它。
      • @MatsPetersson,仅供参考 - 在“C 和 C++ 中的安全编码”(Robert Seacord)第 2 版第 8 章第 5 节中相当广泛地讨论了这种竞争条件。
      • @NathanErnst:这是一个众所周知的问题。但只有当您处理直接具有安全方面的文件(“/etc/passwd”或类似的东西)或应用程序本身具有安全方面时,这才是一个问题。对于大多数应用程序来说,尝试打开以进行读取然后打开以进行写入就足够了,因为无论如何都没有竞争相同的文件名 - 这只是提醒“您已经使用过该名称,请选择另一个”。
      • @MatsPetersson,同意。不幸的是,即使查看 C++11 标准库,似乎也没有办法直接解决这个问题。您不能从文件描述符构造basic_filebuf,因此fopenfstream 不能一起工作。此外,如果文件已经存在,则没有openmode 标志可用于失败(假设您也拥有该文件的权限)。我能看到的最好的方法是以附加模式打开并针对 0 测试 tellp,我什至不确定这是否安全或正确。
      • @NathanErnst:以附加模式打开不会阻止它使用空文件(但存在)。但我认为这里的问题是“支持许多操作系统架构” - 有些操作系统根本不支持以“工作”的方式打开文件(你必须采取其他措施来防止覆盖文件 - 显然,这差不多意味着它不是一个非常安全的系统,但 C++ 作为一种语言不需要安全性...)。
      【解决方案9】:

      试试

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

      发件人:How to check if a file exists and is readable in C++?

      或者你可以使用 boost 函数。

      【讨论】:

      • 如果您没有对该文件的读取权限,这可能不起作用。当然,大多数情况下您无论如何都无法写入 - 但有些系统具有奇怪的权限,您可能拥有没有读取权限的写入权限,从而破坏了您的文件。
      猜你喜欢
      • 2015-02-16
      • 2014-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-13
      • 1970-01-01
      • 2019-03-07
      • 2021-05-31
      相关资源
      最近更新 更多