【问题标题】:Portable way to check if directory exists [Windows/Linux, C]检查目录是否存在的便携式方法 [Windows/Linux, C]
【发布时间】:2013-08-08 15:15:03
【问题描述】:

我想检查给定目录是否存在。我知道如何在 Windows 上执行此操作:

BOOL DirectoryExists(LPCTSTR szPath)
{
  DWORD dwAttrib = GetFileAttributes(szPath);

  return (dwAttrib != INVALID_FILE_ATTRIBUTES && 
         (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}

和 Linux:

DIR* dir = opendir("mydir");
if (dir)
{
    /* Directory exists. */
    closedir(dir);
}
else if (ENOENT == errno)
{
    /* Directory does not exist. */
}
else
{
    /* opendir() failed for some other reason. */
}

但是我需要一种可移植的方式来执行此操作.. 无论我使用什么操作系统,有什么方法可以检查目录是否存在?也许C标准库方式?

我知道我可以使用预处理器指令并在不同的操作系统上调用这些函数,但这不是我要求的解决方案。

至少现在我结束了这个:

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>

int dirExists(const char *path)
{
    struct stat info;

    if(stat( path, &info ) != 0)
        return 0;
    else if(info.st_mode & S_IFDIR)
        return 1;
    else
        return 0;
}

int main(int argc, char **argv)
{
    const char *path = "./TEST/";
    printf("%d\n", dirExists(path));
    return 0;
}

【问题讨论】:

  • 简单地尝试在其中创建一个文件(具有随机文件名)怎么样?
  • 请注意,您从此类测试中获得的任何答案都会立即过时。您刚刚检查的目录可以在您使用它时被删除或移动 - 这种类型的代码本质上是错误的。这种“check-then-use”的bug甚至还有it's own Wikipedia page:“在软件开发中,time-of-check to time-of-use(TOCTOU, TOCTTOU or TOC/TOU)是一类由一种竞争条件,涉及检查系统的一部分(例如安全凭证)的状态以及使用该检查的结果。”
  • 如果您需要在特定目录中创建文件,只需在该目录中创建文件。如果该目录不存在,您将收到错误消息。并且首先检查目录是否存在 NOT 保证您可以创建该文件,因此无论如何您都必须处理创建错误。

标签: c file directory file-exists


【解决方案1】:

使用boost::filesystem,这将为您提供一种可移植的方式来做这些事情并为您抽象出所有丑陋的细节。

【讨论】:

  • 哦,我怀疑在 C 中是否会有一种可移植的方式来做到这一点。您只需要使用#define 来检查您的代码正在为哪个操作系统编译并为您要编译的每个操作系统添加。
  • 当标准库有办法处理时,并不是每个人都愿意为一个简单的函数增加和膨胀他们的代码库
【解决方案2】:

您可以使用GTK glib 从操作系统的东西中抽象出来。

glib 提供了一个 g_dir_open() 函数,它应该可以解决问题。

【讨论】:

    【解决方案3】:

    stat() 也适用于 Linux。、UNIX 和 Windows:

    #include <sys/types.h>
    #include <sys/stat.h>
    
    struct stat info;
    
    if( stat( pathname, &info ) != 0 )
        printf( "cannot access %s\n", pathname );
    else if( info.st_mode & S_IFDIR )  // S_ISDIR() doesn't exist on my windows 
        printf( "%s is a directory\n", pathname );
    else
        printf( "%s is no directory\n", pathname );
    

    【讨论】:

    • 在 Xubuntu 12.04 和 Windows XP 上检查过,它有效,谢谢!
    • @alk:你确定吗?我在我的第一篇文章中添加了一个示例代码,它在没有任何预处理器指令的情况下工作,没有任何警告(我在 Xubuntu 上使用 gcc,在 Windows 上使用 MinGW)
    • @alk 我在发布之前尝试过,不需要预处理器指令
    • @IngoLeonhardt:您确实使用了哪个编译器/环境?
    • 我无法让它在 OSX 10.11 上运行。 XCode7 说:“调用 stat 没有匹配的函数”。解决方法是让我在 stat() 调用中执行 pathname.c_str(),因为我的路径名是 std::string。
    【解决方案4】:

    由于我发现上述批准的答案缺乏明确性,并且操作人员提供了他/她将使用的错误解决方案。因此,我希望以下示例对其他人有所帮助。该解决方案或多或少也具有便携性。

    /******************************************************************************
     * Checks to see if a directory exists. Note: This method only checks the
     * existence of the full path AND if path leaf is a dir.
     *
     * @return  >0 if dir exists AND is a dir,
     *           0 if dir does not exist OR exists but not a dir,
     *          <0 if an error occurred (errno is also set)
     *****************************************************************************/
    int dirExists(const char* const path)
    {
        struct stat info;
    
        int statRC = stat( path, &info );
        if( statRC != 0 )
        {
            if (errno == ENOENT)  { return 0; } // something along the path does not exist
            if (errno == ENOTDIR) { return 0; } // something in path prefix is not a dir
            return -1;
        }
    
        return ( info.st_mode & S_IFDIR ) ? 1 : 0;
    }
    

    【讨论】:

    • 至少在 Linux 上,您还需要为 ENOTDIR 提供特殊处理。
    • @JohnBollinger,感谢您的建议。我已经编辑了回复以包含它,因为它看起来很有意义。但是,在我的测试中,我未能触发这种情况。我不太了解 stat 的手册页和此错误。有人如何在测试中触发这个?
    • ENOTDIR 表示给定路径中的一个名称,除了最后一个,存在但不是目录。因此,尝试统计 /tmp/regular_file/foo 应该会引发此错误,假设 /tmp/regular_file 确实是一个常规文件。这要区别于统计/tmp/non_existent/foo/tmp/foo/non_existant;后两者都应该与ENOENT 出错。
    • 我不会投反对票。但是,如果您要提供一个更清晰的示例,最好还包括必要的包含。整个堆栈溢出页面(您的答案和其他人)都没有提到“#include ”。但从我所做的其他阅读中,我知道这是一个缺失的导入。
    • 在 MSVC 编译中我没有得到错误,在 GCC 编译中我确实得到了它。
    【解决方案5】:

    在 C++17 中,您可以使用 std::filesystem::is_directory 函数 (https://en.cppreference.com/w/cpp/filesystem/is_directory)。它接受一个 std::filesystem::path 对象,该对象可以用 unicode 路径构造。

    【讨论】:

    • 谢谢你,因为这是标准库的一部分,不需要臃肿的 3rd 方库,这应该是公认的答案
    【解决方案6】:

    以上示例未使用可用于 Windows 和 Linux 的 [_access]1。 使用 [stat]2、_access() 和最新文件系统的 [exists]3 进行测试的代码示例 (Windows)。

    #include <iostream>
    #include <Windows.h>
    #include <io.h>
    #include <filesystem>
    #include <vector>
    #include <string>
    
    using namespace std;
    namespace fs = std::filesystem;
    std::vector<std::string> DirPaths
    {
      "G:\\My-Shcool-2021-22",
      "\\\\192.168.111.8\\Oaco\\RotData\\VV-VA",
      "\\\\192.168.111.15\\5500\\C-drive\\Oaco\\RotateEarthProject",   "\\\\192.168.111.18\\d$\\Mercurial\\Workspace\\HideMoon\\Win32\\Debug", 
    "Z:\\SuperSaver" //mapped network drive; symbolic link
    };
    /* test if the path is a directory
    */
    void TestDirExists ()
    {
    int erno =-1;
    struct stat  info {};
    auto ErrorMsg = [&](std::string path) 
    {
        _get_errno(&erno);
        if (erno == EACCES)
            cout << "access denied  " << path << endl;
        else if (erno == ENOENT)
            cout << "dir path not found  " << path << endl;
        else if (erno == EINVAL)
            cout << "invalid parameter  " << path << endl;        
    };
    
    for (const auto &dp : DirPaths)
    {
        erno = -1;
        if (stat(dp.c_str(), &info) != 0)
            ErrorMsg(dp);        
        if (_access(dp.c_str(), 0) != 0)
            ErrorMsg(dp);       
        if (fs::exists(dp)==0)
            ErrorMsg(dp);
        if(erno < 0)
             cout << "#Dir Found: " << dp << endl;
    }
    

    } [1]:https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/access-waccess?view=msvc-170 [2]:https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/stat-functions?view=msvc-170 [3]:https://en.cppreference.com/w/cpp/filesystem/exists

    【讨论】:

      猜你喜欢
      • 2023-03-17
      • 2023-04-08
      • 2020-09-13
      • 1970-01-01
      • 2013-11-16
      • 1970-01-01
      • 2012-03-08
      • 2010-10-12
      • 1970-01-01
      相关资源
      最近更新 更多