Подтвердить что ты не робот

Как проверить, существует ли файл на С++ портативным способом?

В настоящее время я использую этот код, чтобы проверить, существует ли файл на Windows и POSIX -совместимых операционных системах (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;
}

Вопросы:

  • Есть ли у этого кода какие-то подводные камни? (возможно, ОС, где он не может быть скомпилирован)

  • Возможно ли это сделать по-настоящему портативно, используя только стандартную библиотеку C/С++?

  • Как его улучшить? Ищите канонический пример.

4b9b3361

Ответ 1

Поскольку С++ также помечен, я бы использовал boost::filesystem:

#include <boost/filesystem.hpp>

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

За кулисами

По-видимому, boost использует stat в POSIX и DWORD attr(::GetFileAttributesW(FileName)); в Windows (Примечание: здесь я извлек соответствующие части кода, возможно, я сделал что-то неправильно, но это должно быть так).

В основном, помимо возвращаемого значения, boost проверяет значение errno, чтобы проверить, действительно ли файл не существует, или ваш stat не удалось по другой причине.

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

Ответ 2

Мне хотелось бы просто открыть файл:

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

должен работать на все, что имеет файлы [не требуемые стандартом С++], и поскольку он использует С++ std::string, я не понимаю, почему проблема std::ifstream должна быть проблемой.

Ответ 3

  • Есть ли у этого кода какие-то подводные камни? (возможно, ОС, где он не может быть скомпилирован)

Result == 0 "пропускает" ENAMETOOLONG, ELOOP, ошибки и т.д. по this

Я могу думать об этом: путь ENAMETOOLONG слишком длинный: -

Во многих случаях во время рекурсивного сканирования подпапка/каталоги продолжают расти, если путь слишком длинный, это может привести к этой ошибке, но файл все еще существует!

Аналогичные случаи могут возникать и с другими ошибками.

Кроме того,

В соответствии с этим, Я предпочел использовать перегруженный метод boost::filesystem::exists

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