20

我从升级#include <experimental/filesystem>#include <filesystem>. 似乎该std::filesystem::path::wstring方法没有返回与 in 相同的字符串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吗?

4

3 回答 3

11

粗略地说,当编译器表现出标准禁止的行为(显式或隐式)或与所述编译器的文档不同的行为时,就会发生编译器中的错误。

该标准对本机路径字符串的格式没有任何限制,只是底层操作系统应该接受该格式(以下引用)。它怎么能施加这样的限制?该语言对主机操作系统如何处理路径没有发言权,并且要自信地做到这一点,它必须知道它可能编译到的每一个目标,这显然是不可行的。

[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命名空间就是它在包装盒上写的:你不应该期望最终标准化库的行为与其实验版本相同,或者甚至期望最终标准化库会存在。在处理实验性的东西时,它就是这样。

于 2018-08-31T20:33:30.027 回答
7

不,这不是错误!

string()et alc_str()/以native()本机路径名格式返回内部路径名。

本地人是什么意思

MS 声明,它使用ISO/IEC TS 18822:2015。最终草案在 §4.11 中定义了本机路径名格式,如下所示:

主机操作系统接受的依赖于操作系统的路径名格式。

在 Windows 中,native()将路径返回为std::wstring().

如何在 Windows 中强制使用反斜杠作为目录分隔符

该标准定义了术语 首选分隔符(另见§8.1(路径名格式语法)):

依赖于操作系统的目录分隔符。

可以使用 . 将路径转换(就地)为首选分隔符path::make_preferred。在 Windows 中,它具有noexcept运算符。

为什么你不应该担心

关于路径的MS 文档说明了/vs的使用\

Windows API 中的文件 I/O 函数将“/”转换为“\”作为将名称转换为 NT 样式名称的一部分,除非使用“\?\”前缀,如以下部分所述。

有关 C++ 文件导航的文档中,斜杠(在较新的草案中称为fallback-separator)甚至直接在root-name之后使用:

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

因此,只有在使用强制使用该分隔符进行文件系统交互的库时,才需要对首选分隔符进行显式路径转换。

于 2018-09-04T10:47:56.927 回答
5

其中任何一个都可以被认为是平台上的“本地”,因此这些选项中的任何一个都同样有效。文件系统 API 不保证“本机”版本与您提供的字符串相同,无论平台如何。如果通用“/”字符与其等效,也不能保证“本机”字符串将仅使用本机目录分隔符。

于 2018-08-17T02:13:50.250 回答