web-dev-qa-db-ja.com

ファイルがC ++に移植可能に存在するかどうかを確認するにはどうすればよいですか?

現在、このコードを使用して、ファイルがWindowsおよびPOSIX互換のOS(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;
}

質問:

  1. このコードには落とし穴がありますか? (たぶんコンパイルできないOS)

  2. C/C++標準ライブラリのみを使用して、真に移植可能な方法でそれを行うことは可能ですか?

  3. それを改善する方法は?正規の例を探しています。

13
Sergey K.

C++にもタグが付けられているので、_boost::filesystem_を使用します。

_#include <boost/filesystem.hpp>

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

舞台裏

どうやら、boostはPOSIXではstatを使用し、WindowsではDWORD attr(::GetFileAttributesW(FileName));を使用しているようです(注:ここでコードの関連部分を抽出しました。何か間違ったことをした可能性がありますが、これはそれ)。

基本的に、boostは戻り値に加えて、ファイルが実際に存在しないかどうか、または別の理由で統計が失敗したかどうかを確認するためにerrno値をチェックしています。

_#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_は、WindowsとPOSIXで別々に定義されています。

ウィンドウズ:

_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;
  }
_
20
Nemanja Boric

私はいつもファイルを開こうとするのが好きです:

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

[C++標準では必要ない]ファイルがあるものなら何でも動作するはずです。C++ std::stringを使用しているので、std::ifstreamが問題になる理由がわかりません。

2
Mats Petersson
  1. このコードには落とし穴がありますか? (たぶんコンパイルできないOS)

_Result == 0_ "skips" ENAMETOOLONGELOOP、エラーなど this

私はこれを考えることができます:ENAMETOOLONGパスは長すぎます:-

多くの場合、再帰スキャン中にサブフォルダ/ディレクトリは増加し続けます。パスが「長すぎる」場合、このエラーが発生する可能性がありますが、ファイルはまだ存在します。

同様のケースが他のエラーでも発生する可能性があります。

また、

this のように、オーバーロードされた_boost::filesystem::exists_メソッドを使用することをお勧めします

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

1
P0W