C ++ 17 std :: filesystem :: path中的本机路径分隔符错误?

时间:2018-08-16 22:22:04

标签: c++ c++17 std-filesystem

#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中的错误吗?

3 个答案:

答案 0 :(得分:9)

大致上,编译器中的错误是指它表现出标准(显式或隐式)所禁止的行为,或与所述编译器文档不同的行为。

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

  

[fs.class.path]

     

5路径名是代表路径名的字符串。   路径名是根据通用路径名格式语法([fs.path.generic])或主机操作系统接受的依赖于操作系统的本机路径名格式设置的格式。

>

(强调我的)

The documentation of 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命名空间就是在包装盒上说的:您不应该期望最终的标准化库的行为与其实验版本相同,甚至不希望最终的标准化库将完全存在。 。处理实验性内容时就是这样。

答案 1 :(得分:6)

不,这不是bug!

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

本地人是什么意思

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

  

与操作系统有关的路径名格式主机操作系统接受的

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

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

该标准定义了术语  preferred-separator(另请参见§8.1 (pathname format grammar)):

  

取决于操作系统的目录分隔符。

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

为什么你不应该担心

MS documentation about paths说明了/\的用法

  

Windows API中的文件I / O函数将名称转换为NT样式名称的过程中将“ /”转换为“ \”,除非使用以下部分中详细说明的“ \?\”前缀。

,在documentation about C++ file navigation中,斜杠(在新的草稿中称为fallback-separator)甚至在root-name之后直接使用:

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

带有-std:C++17的VS2017 15.8的示例:

#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

因此,只有在使用强制使用分隔符进行文件系统交互的库时,才需要将显式路径转换为 preferred-separator

答案 2 :(得分:5)

其中任何一个在平台上都可以视为“本机”,因此这些选项中的任何一个都同样有效。 Filesystem API不保证“本机”版本将与您提供的字符串相同,而不管平台如何。也不保证“本地”字符串仅在通用“ /”字符等效时才使用本地目录分隔符。