C++ - определение, существует ли каталог (не файл) в Linux

Как определить, существует ли каталог (не файл) с использованием С++ в Linux? Я попытался использовать функцию stat(), но она вернула положительный результат, когда файл был найден. Я только хочу узнать, является ли введенная строка каталогом, а не чем-то еще.


person MetaDark    schedule 12.02.2011    source источник
comment
stat() должно работать. Как вы его использовали?   -  person John Bartholomew    schedule 13.02.2011
comment
структура стат ст; cout ‹‹ stat(input,&st) ‹‹ endl; if(stat(input,&st) != 0) { ... } - И каталог, и файл возвращают 0 при cout-ed.   -  person MetaDark    schedule 13.02.2011
comment
Imho флаг дублирования неверен, так как другой вопрос касается поиска системного вызова, а этот вопрос касается общих способов в C++.   -  person Roi Danton    schedule 26.08.2018


Ответы (6)


Согласно man(2) stat, вы можете использовать макрос S_ISDIR в поле st_mode:

bool isdir = S_ISDIR(st.st_mode);

Примечание: я бы рекомендовал использовать Boost и/или Qt4, чтобы упростить кросс-платформенную поддержку, если ваше программное обеспечение может работать на других ОС.

person OneOfOne    schedule 12.02.2011
comment
После включения ‹sys/types.h›, ‹sys/stat.h› и ‹unistd.h› я получаю ошибку компилятора, g++ сообщает об ошибке: «S_IDDIR» не был объявлен в этой области. Кто-нибудь знает, что может происходить? - person bchurchill; 21.10.2014
comment
Бахах. Опечатка. S_IDDIR -> S_ISDIR. - person bchurchill; 21.10.2014

как насчет того, что я нашел здесь

#include <dirent.h>

bool DirectoryExists( const char* pzPath )
{
    if ( pzPath == NULL) return false;

    DIR *pDir;
    bool bExists = false;

    pDir = opendir (pzPath);

    if (pDir != NULL)
    {
        bExists = true;    
        (void) closedir (pDir);
    }

    return bExists;
}

Или используя стат.

struct stat st;
if(stat("/tmp",&st) == 0)
    if(st.st_mode & S_IFDIR != 0)
        printf(" /tmp is present\n");
person ayush    schedule 12.02.2011
comment
Это может быть не так уж плохо, но пример, приведенный выше, на самом деле не так эффективен, и нижний пример — это то, что я уже использую, за исключением того, что я использую != вместо == - person MetaDark; 13.02.2011
comment
Да, нижний нужно обновить с дополнительным условием DarkDust and. (myStat.st_mode) & S_IFMT) == S_IFDIR). спасибо ДаркДаст. - person ayush; 13.02.2011
comment
Пример, приведенный выше, является единственным, который работает для меня, поэтому я собираюсь принять его в качестве ответа на данный момент. - person MetaDark; 13.02.2011
comment
Нет необходимости пробовать opendir в каталоге; используйте макрос S_ISDIR. - person MarkR; 13.02.2011
comment
Вы говорите, что если каталог существует, он должен быть if(st.st_mode & S_IFDIR == 0) ??? - person Michele; 20.10.2016


Я так понимаю ваш вопрос: у вас есть путь, скажем, /foo/bar/baz (баз — это файл), и вы хотите знать, существует ли /foo/bar. Если это так, решение выглядит примерно так (непроверено):

char *myDir = dirname(myPath);
struct stat myStat;
if ((stat(myDir, &myStat) == 0) && (((myStat.st_mode) & S_IFMT) == S_IFDIR)) {
    // myDir exists and is a directory.
}
person DarkDust    schedule 12.02.2011
comment
Нет, я имею в виду, что /foo/bar/baz и /foo/bar/baz используют одно и то же, это будет рассматриваться как одна и та же строка, но один baz — это каталог, а другой — файл. Используя stat(), он только сообщает мне, существует ли строка, а не файл или каталог. - person MetaDark; 13.02.2011
comment
Как такое вообще могло случиться? У вас не может быть двух разных записей каталога с одинаковым именем; либо /foo/bar/baz не существует, либо он существует и является каталогом, либо существует и не является каталогом; он не может существовать как одновременно как каталог и не как каталог. - person John Bartholomew; 13.02.2011
comment
Ах, так вы хотите знать, что это такое. В этом случае ответ OneOfOne — это то, что вам нужно. - person John Bartholomew; 13.02.2011
comment
Это также правильный ответ, за исключением того, что использование st_mode & S_IFMT вместо S_ISDIR является плохим стилем. - person zwol; 13.02.2011
comment
@MetaDark: stat делает говорит вам, является ли путь каталогом, в флагах st_mode. Либо используйте мой код для проверки флага каталога, либо макроса S_ISDIR, как показано в ответе OneOfOne. - person DarkDust; 13.02.2011
comment
Я люблю C++, он интуитивно понятен, его легко запомнить и он работает! - person Andrzej Rehmann; 11.03.2014

В C++17** std::filesystem предоставляет два варианта определения существования дорожка:

  1. is_directory() определяет, является ли путь каталогом и существует ли он в реальной файловой системе.
  2. exists() просто определяет, существует ли путь в реальной файловой системе (не проверяя, если это каталог)

Пример (без обработки ошибок):

#include <iostream>
#include <filesystem> // C++17
//#include <experimental/filesystem> // C++14
namespace fs = std::filesystem;
//namespace fs = std::experimental::filesystem; // C++14

int main()
{
    // Prepare.
    const auto processWorkingDir = fs::current_path();
    const auto existingDir = processWorkingDir / "existing/directory"; // Should exist in file system.
    const auto notExistingDir = processWorkingDir / "fake/path";
    const auto file = processWorkingDir / "file.ext"; // Should exist in file system.

    // Test.
    std::cout
        << "existing dir:\t" << fs::is_directory(existingDir) << "\n"
        << "fake dir:\t" << fs::is_directory(notExistingDir) << "\n"
        << "existing file:\t" << fs::is_directory(file) << "\n\n";

    std::cout
        << "existing dir:\t" << fs::exists(existingDir) << "\n"
        << "fake dir:\t" << fs::exists(notExistingDir) << "\n"
        << "existing file:\t" << fs::exists(file);
}

Возможный вывод:

existing dir:   1
fake dir:       0
existing file:  0

existing dir:   1
fake dir:       0
existing file:  1

**в C++14 доступен std::experimental::filesystem


Обе функции выдают filesystem_error в случае ошибок. Если вы хотите избежать перехвата исключений, используйте перегруженные варианты с std::error_code в качестве второго. параметр.

#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;

bool isExistingDir(const fs::path& p) noexcept
{
    try
    {
        return fs::is_directory(p);
    }
    catch (std::exception& e)
    {
        // Output the error message.
        const auto theError = std::string{ e.what() };
        std::cerr << theError;

        return false;
    }
}

bool isExistingDirEC(const fs::path& p) noexcept
{
    std::error_code ec;
    const auto isDir = fs::is_directory(p, ec);
    if (ec)
    {
        // Output the error message.
        const auto theError = ec.message();
        std::cerr << theError;

        return false;
    }
    else
    {
        return isDir;
    }
}

int main()
{
    const auto notExistingPath = fs::path{ "\xa0\xa1" };
    isExistingDir(notExistingPath);
    isExistingDirEC(notExistingPath);
}
person Roi Danton    schedule 13.04.2017
comment
Зачем сначала использовать is_directory(), а потом exists()? Кроме того, действительно ли нужен exists()? - person Andrew; 26.08.2018
comment
В Windows я сделал: if (!is_directory("myDir")) { create_directory("myDir"); if (!is_directory("myDir")) { return false; } } и все работало нормально (создавал папку, если она не существовала, не создавал ее, если она существовала в виде папки, возвращал false, если она существовала в виде файла). - person Andrew; 26.08.2018
comment
@Эндрю Спасибо за подсказку, exists() действительно было лишним! Я обновил пример. Поскольку вопрос не включает создание каталога, я опускаю это в ответе. - person Roi Danton; 26.08.2018

Если вы хотите узнать, существует ли каталог, потому что вы хотите что-то сделать с ним, если он существует (создать файл/каталог внутри, отсканировать его содержимое и т. д.), вы должны просто пойти дальше и сделать все, что хотите, затем проверьте если это не удалось, и если да, сообщите strerror(errno) пользователю. Это общий принцип программирования под Unix: не пытайтесь выяснить, будет ли работать то, что вы хотите сделать. Попробуйте, а потом посмотрите, не получилось ли.

Если вы хотите вести себя особым образом, если что-то не удалось, потому что каталог не существует (например, если вы хотите создать файл и все необходимые содержащие каталоги), вы проверяете errno == ENOENT после сбоя open.

Я вижу, что один респондент рекомендовал использовать boost::filesystem. Я хотел бы поддержать эту рекомендацию, но, к сожалению, не могу, потому что boost::filesystem предназначен не только для заголовков, а все модули Boost, не предназначенные только для заголовков, имеют ужасный послужной список вызывающих загадочные поломки при обновлении. общую библиотеку без перекомпиляции приложения или даже если вам просто не удалось скомпилировать приложение с точно теми же флагами, которые использовались для компиляции общей библиотеки. Горе обслуживания просто не стоит.

person zwol    schedule 12.02.2011
comment
Если он обнаружит его как файл, он все равно продолжит работу и создаст, по-видимому, поврежденный tar-файл. Ах да, если я еще не сказал, я пытаюсь смолить каталог. - person MetaDark; 13.02.2011
comment
Первым шагом в создании архива из каталога является сканирование каталога, так почему бы вам просто не вызвать opendir и посмотреть, не сработает ли он (что должно произойти при применении к обычному файлу)? - person zwol; 13.02.2011