How to check if file exists in C++ in a portable way?

半腔热情 提交于 2019-11-27 23:46:27

问题


Currently I use this code to check if file exists on Windows and POSIX-compatible OSes (Linux, Android, MacOS, iOS, BlackBerry 10):

bool FileExist( const std::string& Name )
{
#ifdef OS_WINDOWS
    struct _stat buf;
    int Result = _stat( Name.c_str(), &buf );
#else
    struct stat buf;
    int Result = stat( Name.c_str(), &buf );
#endif
    return Result == 0;
}

Questions:

  1. Does this code have any pitfalls? (maybe an OS where it cannot be compiled)

  2. Is it possible to do it in a truly portable way using only C/C++ standard library?

  3. How to improve it? Looking for canonical example.


回答1:


Because C++ is also tagged, I would use boost::filesystem:

#include <boost/filesystem.hpp>

bool FileExist( const std::string& Name )
{
     return boost::filesystem::exists(Name);
}

Behind the scenes

Apparently, boost is using stat on POSIX and DWORD attr(::GetFileAttributesW(FileName)); on Windows (Note: I've extracted the relevant parts of code here, it could be that I did something wrong, but this should be it).

Basically, besides return value, boost is checking errno value in order to check if file really does not exist, or your stat failed for a different reason.

#ifdef BOOST_POSIX_API

struct stat path_stat;
if (::stat(p.c_str(), &path_stat)!= 0)
{
  if (ec != 0)                            // always report errno, even though some
    ec->assign(errno, system_category());   // errno values are not status_errors

  if (not_found_error(errno))
  {
    return fs::file_status(fs::file_not_found, fs::no_perms);
  }
  if (ec == 0)
    BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::status",
      p, error_code(errno, system_category())));
  return fs::file_status(fs::status_error);
}

#else
     DWORD attr(::GetFileAttributesW(p.c_str()));
     if (attr == 0xFFFFFFFF)
     {
         int errval(::GetLastError());
         if (not_found_error(errval))
         {
             return fs::file_status(fs::file_not_found, fs::no_perms);
         }
     }   
#endif

not_found_error is defined separately for Windows and for POSIX:

Windows:

bool not_found_error(int errval)
  {
    return errval == ERROR_FILE_NOT_FOUND
      || errval == ERROR_PATH_NOT_FOUND
      || errval == ERROR_INVALID_NAME  // "tools/jam/src/:sys:stat.h", "//foo"
      || errval == ERROR_INVALID_DRIVE  // USB card reader with no card inserted
      || errval == ERROR_NOT_READY  // CD/DVD drive with no disc inserted
      || errval == ERROR_INVALID_PARAMETER  // ":sys:stat.h"
      || errval == ERROR_BAD_PATHNAME  // "//nosuch" on Win64
      || errval == ERROR_BAD_NETPATH;  // "//nosuch" on Win32
  }

POSIX:

bool not_found_error(int errval)
  {
    return errno == ENOENT || errno == ENOTDIR;
  }



回答2:


I perosnally like to just try to open the file:

bool FileExist( const std::string& Name )
{
     std::ifstream f(name.c_str());  // New enough C++ library will accept just name
     return f.is_open();
}

should work on anything that has files [not required by the C++ standard] and since it's using C++ std::string, I don't see why std::ifstream should be a problem.




回答3:


  1. Does this code have any pitfalls? (maybe an OS where it cannot be compiled)

Result == 0 "skips" ENAMETOOLONG , ELOOP, errors etc. as per this

I can think of this : ENAMETOOLONG path is too long as:-

In many cases ,during a recursive scan, the sub-folder/directories keep on increasing, if path is "too" long it may result into this error, but still file do exists !

Similar cases might happen with other errors too.

Also,

As per this, I'ld prefer to use the overloaded boost::filesystem::exists method

bool exists(const path& p, system::error_code& ec) noexcept;



来源:https://stackoverflow.com/questions/18320486/how-to-check-if-file-exists-in-c-in-a-portable-way

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!