Как корневой путь std::filesystem может быть родителем?

Эта программа:

#include <iostream>
#include <filesystem>

int main()
{
    std::filesystem::path p1("c:\\");
    std::filesystem::path p2("c:/");
    if (p1.has_parent_path())
        std::cout << "Parent path of " << p1 << " is " << p1.parent_path() << std::endl;
    if (p2.has_parent_path())
        std::cout << "Parent path of " << p2 << " is " << p2.parent_path() << std::endl;
}

Производит этот вывод:

Parent path of "c:\\" is "c:\\"
Parent path of "c:/" is "c:/"

(РЕДАКТИРОВАТЬ: возникла путаница по поводу использования косой черты, поэтому я обновил этот код, чтобы показать, что одно и то же происходит независимо от того, какие разделители пути вы используете в Windows)

Это не имеет смысла для меня. Как каталог может быть родителем самого себя? Какой смысл даже иметь функцию has_parent, если она никогда не вернет false?

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

(Я использую Visual Studio 2019 в языковом режиме С++ 17, если это имеет значение)


person Joe    schedule 02.10.2019    source источник
comment
чтобы обнаружить, что я попал в корневую папку и должен остановиться из приведенного выше, и ответ Щелкунчика похоже, что проверка, совпадает ли p.parentPath() с p, должна работать. Предположительно, has_parent_path() из isolated_file.name вернет false. (Обе идеи не проверены).   -  person TripeHound    schedule 02.10.2019


Ответы (2)


На самом деле в примере с godbolt нет вывода, потому что тест выполняется на GCC в среде posix, где "C:\\" не является корневым каталогом, поэтому он рассматривается как странное имя каталога/файла, а его родитель пуст, но с правильный корневой путь posix "/" это дало бы результат, как наблюдает OP. Так что нет, parent_path() корневого каталога ("/" или "C:\\") не пуст, поэтому has_parent_path() истинен во всех известных мне реализациях std::filesystem.

Фактический стандарт говорит: "Возвращает: *this, если has_relative_path() равно false, в противном случае путь, имя пути универсального формата которого является самым длинным префиксом имени пути универсального формата *this, который производит на один элемент меньше в своей итерации." и relative_path() — это все после root_path(), поэтому relative_path() в данном случае нет, поэтому parent_path() возвращает *this, а has_parent_path() возвращает true, как в примере с OP.

Мое предположение на вопрос, почему стандарт выбрал такое поведение, заключается в том, что это то, что cd .. делает в каждой ОС, если вы уже находитесь в корне, вы остаетесь в том же корне.

Действительно, один из возможных способов определения конца: (!p.has_parent_path() || !p.has_relative_path()) или, может быть, даже !p.has_relative_path(), в зависимости от того, хотите ли вы закончить пустым путем, если он был без корней.

person Gulrak    schedule 04.10.2019
comment
Спасибо. Это имеет смысл. В моем собственном коде моим обходным путем было добавление проверки, чтобы увидеть, если parent_path() == root_path(), так как это также имело место в этом условии. Я понятия не имею, моя идея или ваша идея более надежны или они одинаковы. - person Joe; 04.10.2019

Функция bool has_parent_path() const; проверяет, является ли путь, возвращаемый функцией path parent_path() const;, пустым.

Кроме того, для функции path parent_path() const; стандарт гласит:

Возвращает корневой каталог пути универсального формата. Если путь (в универсальном формате) не включает корневой каталог, возвращает path().

В системах Linux, где корневой путь равен /, и в системах Windows, где корневой путь равен C:\\, функция parent_path() вернет пустую строку, поэтому, следовательно, функция has_parent_path() вернет false.

Проверьте этот код из здесь:

#include <iostream>
#include <experimental/filesystem>

namespace fs = std::experimental::filesystem;

int main() {
    for (fs::path p : {"/var/tmp/example.txt", "/", "C:\\", "/var/tmp/."}) {
        std::cout << "The parent path of " << p
                  << " is " << p.parent_path() << '\n';
        std::cout << "Has parent path: " << p.has_parent_path() << '\n';
    }

    return 0;
}

Обратите внимание на указание корневого каталога Windows. Это не c:/, а C:\\.

person NutCracker    schedule 02.10.2019
comment
Значит, поведение в Windows и Linux отличается? - person Hengqi Chen; 02.10.2019
comment
Нет, я пропустил одну вещь, поэтому проверьте еще раз - person NutCracker; 02.10.2019
comment
Корень диска C — это C:\, но \ — это escape-символ для строковых литералов. Вы можете написать это как R"(C:\)" - person Caleth; 02.10.2019
comment
На самом деле c:/ вполне подходит для использования в качестве корневого диска в Windows. Косая черта — это не escape-символ, и она принимается файловыми функциями. Я обычно предпочитаю это бэкслахам - person Joe; 02.10.2019
comment
@Caleth, я знаю, но ОП написал косую черту - person NutCracker; 02.10.2019
comment
Я ОП. Если я изменю свой код, чтобы использовать две обратные косые черты, поведение будет идентичным - person Joe; 02.10.2019
comment
Чтобы было ясно, я прочитал стандарт, прежде чем опубликовать это. Ваше утверждение о том, что путь c:\\ вернет пустой родительский путь в Windows, неверно. Мой вопрос не в том, почему мой код ведет себя так, как говорит стандарт, а в том, почему стандарт имеет такое бессмысленное поведение. - person Joe; 02.10.2019
comment
Прости, я не знаю, что с этим делать. Есть ли где-то кнопка запуска, которую мне не хватает? Когда я запускаю этот точный код в Visual Studio 2019 на своем компьютере (я просто скопировал его), он сообщает мне, что введенный вами путь действительно имеет родителя, который сам является - person Joe; 02.10.2019
comment
Третий столбец показывает результат работы программы. Вам не нужно запускать его - person NutCracker; 02.10.2019
comment
На самом деле это не так. В третьем столбце показаны следующие 3 предложения: компилятор генерации ASM вернул: 0, компилятор сборки выполнения вернул: 0, а программа вернула 0. Он не показывает никаких выходных данных std::cout. - person Joe; 02.10.2019
comment
Потому что выхода нет. Если условие не выполняется - person NutCracker; 02.10.2019
comment
В любом случае, когда я запускаю его на настоящем компиляторе MSVC, я получаю то, что получаю. - person Joe; 02.10.2019