Ошибка разделителя собственного пути в C++17 std::filesystem:: path?

я столкнулся с проблемой при обновлении с #include <experimental/filesystem> to #include <filesystem>. Кажется, что std::filesystem::path::wstring метод не возвращает ту же строку, как и в experimental::filesystem. Я написал следующую небольшую тестовую программу с включенным результатом вывода.

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

namespace fs = std::filesystem;
namespace ex = std::experimental::filesystem;
using namespace std;

int main()
{
    fs::path p1{ L"C:temp/foo" };    
    wcout << "std::filesystem Native: " << p1.wstring() << "  Generic: " << p1.generic_wstring() << endl;

    ex::path p2{ L"C:temp/foo" };
    wcout << "std::experimental::filesystem Native: " << p2.wstring() << "  Generic: " << p2.generic_wstring() << endl;
}

/* Output:
std::filesystem Native: C:temp/foo  Generic: C:/temp/foo
std::experimental::filesystem Native: C:tempfoo  Generic: C:/temp/foo
*/

согласно https://en.cppreference.com/w/cpp/filesystem/path/string:

возвращаемое значение

внутренний путь в родной путь формат, преобразовано в указанную строку тип.

программа работала в Windows 10 и была скомпилирована с Visual Studio 2017 версии 15.8.0. Я ожидал бы, что родной путь будет C:tempfoo.

вопрос: это ошибка в std::filesystem::path?

3 ответов


нет, это не ошибка!

string()et al и c_str()/native() верните внутренний путь в уроженца путь.

что значит родной

MS состояния, он использует ISO / IEC TS 18822: 2015. Окончательный проект определяет собственный формат пути в §4.11 следующим образом:

зависимый путь операционной системы формат принято операционной системой хоста.

В Windows, native() возвращает путь в виде std::wstring().

как принудительно использовать обратные косые черты в качестве разделителя каталогов в Windows

стандарт определяет термин предпочтительный-разделитель (см. Также §8.1 (грамматика формата пути)):

разделитель каталогов, зависящий от операционной системы характер.

путь может быть преобразован (на месте) в предпочтительный разделитель с path::make_preferred. В Windows он имеет noexcept оператора.

почему вы не должны беспокоиться

на MS документация о путях состояния об использовании / vs \

функции ввода-вывода файлов в API Windows преобразуют " / " в " \ " как часть преобразования имени в имя в стиле NT, за исключением случаев использования в "\?\" префикс как описано в следующих разделах.

и документация по навигации по файлам c++, Слэш (известный как запасной вариант-разделитель в новых проектах) даже используется непосредственно после корень-имя:

path pathToDisplay(L"C:/FileSystemTest/SubDir3/SubDirLevel2/File2.txt ");

пример для VS2017 15.8 с -std:C++17:

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

void output(const std::string& type, fs::path& p)
{
    std::cout
        << type << ":\n"
        << "- native: " << p.string() << "\n"
        << "- generic: " << p.generic_string() << "\n"
        << "- preferred-separator" << p.make_preferred() << "\n";
}

int main()
{
    fs::path local_win_path("c:/dir/file.ext");
    fs::path unc_path("//your-remote/dir/file.ext");

    output("local absolute win path", local_win_path);
    output("unc path", unc_path);

    unc_path = "//your-remote/dir/file.ext"; // Overwrite make_preferred applied above.
    if (fs::is_regular_file(unc_path))
    {
        std::cout << "UNC path containing // was understood by Windows std filesystem";
    }
}

возможный вывод (когда unc_path-это существующий файл на существующем пульт ДУ):

local absolute win path:
- native: c:/dir/file.ext
- generic: c:/dir/file.ext
- preferred-separator"c:\dir\file.ext"
unc path:
- native: //your-remote/dir/file.ext
- generic: //your-remote/dir/file.ext
- preferred-separator"\\your-remote\dir\file.ext"
UNC path containing // was understood by Windows std filesystem

так явные преобразования пути к предпочтительный-разделитель должно быть необходимо только при работе с библиотеками, которые обеспечивают использование, что сепаратор для взаимодействия с файловой системой.


грубо говоря, ошибка в компиляторе происходит, когда он демонстрирует поведение, запрещенное стандартом (явно или неявно), или поведение, которое расходится с документацией указанного компилятора.

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

[fs.класс.путь]

5 путь-это символьная строка, представляющая имя пути. Имена путей форматируются в соответствии с грамматикой универсального формата пути ([fs.путь.универсальный]) или в соответствии с зависимый от операционной системы собственный формат пути принято операционной системой хоста.

(выделено мной)

документация MSVC подразумевает, что прямая косая черта вполне приемлема в качестве разделителя:

общим для обеих систем является структура, наложенная на путь, как только вы пройдете корневое имя. По пути c:/abc/xyz/def.ext:

  • корневым имя c:.
  • корневой каталог-это /.
  • корень путь c:/.
  • относительный путь abc/xyz/def.ext.
  • родительский путь c:/abc/xyz.
  • имя файла def.ext.
  • стебель def.
  • расширение .ext.

он упоминает предпочтительный разделитель, но это действительно означает только поведение std::make_preferred, а не путь по умолчанию вывод:

незначительная разница предпочтительный разделитель, между последовательностью каталогов в пути. Обе операционные системы позволяют писать косую черту /, но в некоторых контекстах Windows предпочитает обратную косую черту \.

вопрос о том, является ли это ошибкой, тогда прост: поскольку стандарт не накладывает никаких ограничений на поведение, а документация компилятора не подразумевает обязательной необходимости в обратном Слэш, жучка быть не может.

оставила вопрос о том, является ли это качество реализации вопрос. В конце концов, ожидается, что компилятор и разработчики библиотек будут знать все причуды о своей цели и соответственно реализовывать функции.

это для обсуждения, какой Слэш ('\' или '/') вы должны использовать в Windows, или это действительно имеет значение вообще, так что не может быть никакого авторитетного ответа. Любой ответ, который выступает за один или другие должны быть очень осторожны, чтобы не быть слишком много мнения. Кроме того, само существование path::make_preferred указывает, что собственный путь не обязательно является предпочтительным. Рассмотрим ноль-накладные принцип: сделать путь всегда предпочтительным будет нести накладные расходы на людей, которые не должны быть такими педантичными при обработке путей.

наконец,std::experimental пространство имен - это то, что он говорит на коробке: вы не должны ожидать, что окончательная стандартизированная библиотека вести себя так же, как его экспериментальная версия, или даже ожидать, что окончательная стандартизированная библиотека будет существовать вообще. Просто так бывает, когда имеешь дело с экспериментами.


любой из них можно считать "родным" на платформе, поэтому любой из этих вариантов одинаково действителен. API файловой системы не гарантирует, что" родная " версия будет идентична строке, которую вы ей дали, независимо от платформы. Также нет гарантии, что строка" native "будет использовать только собственный разделитель каталогов, если общий символ" / " эквивалентен ему.