Я столкнулся с проблемой при обновлении с #include <experimental/filesystem>
в #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:\temp\foo Generic: C:/temp/foo
*/
В соответствии с https://en.cppreference.com/w/cpp/filesystem/path/string:
Возвращаемое значение
Внутренний путь в собственном формате,
преобразован в указанный тип строки.
Программа работала на Windows 10 и была скомпилирована с Visual Studio 2017 версии 15.8.0. Я ожидаю, что родной путь будет C:\temp\foo
,
Вопрос: это ошибка в std::filesystem::path
?
Нет, это не ошибка!
string()
и другие а также c_str()
/native()
вернуть внутренний путь в родные формат пути
MS заявляет, оно использует ISO / IEC TS 18822: 2015. Окончательный проект определяет собственный формат пути в §4.11 следующим образом:
Формат пути, зависящий от операционной системы принимается операционной системой хоста.
В Windows native()
возвращает путь как std::wstring()
,
Стандарт определяет термин
Предпочтительный сепаратор (смотрите также §8.1 (грамматика формата имени пути)):
Символ разделителя каталога, зависящий от операционной системы.
Путь может быть преобразован (на месте) в предпочтительный разделитель с помощью path::make_preferred
. В Windows это имеет noexcept
оператор.
MS документация о путях говорится об использовании /
против \
Функции файлового ввода-вывода в Windows API преобразуют «/» в «\» как часть преобразования имени в имя в стиле 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.class.path]
5 Путь — это символьная строка, представляющая имя пути.
Имена путей форматируются в соответствии с общей грамматикой формата имени пути ([fs.path.generic]) или в соответствии с зависимый от операционной системы собственный формат пути, принятый операционной системой хоста.
(Акцент мой)
Документация 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-интерфейс файловой системы не гарантирует, что «нативная» версия будет идентична заданной вами строке независимо от платформы. Также нет гарантии, что «нативная» строка будет использовать нативный разделитель каталогов только в том случае, если ему соответствует общий символ «/».