【问题标题】:Using std::filesystem::path to convert between separator types使用 std::filesystem::path 在分隔符类型之间进行转换
【发布时间】:2019-02-04 23:50:02
【问题描述】:

我正在编写涉及从路径加载文件的代码,该路径构造为从另一个文件加载的给定“基本”路径和辅助相对路径的串联。例如(在我遇到问题的地方),基本路径是“assets/models/”,辅助路径是“maps\map.png”。 这两个字符串的直接连接给出了“assets/models/maps\map.png”。在 POSIX 系统上运行时,无法加载。到目前为止,我一直在通过将反斜杠替换为正斜杠来解决这个问题

std::replace( path.begin(), path.end(), '\\', '/' );

但我想使用 C++17 的 std::filesystem::path 来代替。

std::filesystem::path::make_preferred() 的描述建议它应该替换分隔符:

"将路径的通用格式视图中的所有目录分隔符转换为首选目录分隔符。 例如,在 Windows 上,其中 \ 是首选分隔符,路径 foo/bar 将转换为 foo\bar"

但是,当在代码中实现时,它不会转换任何内容。我还验证了 std::filesystem::path::preferred_separator 是否符合预期 - '/'。

我是否误解了make_preferred() 的目的?还是我用错了?

这是一个不工作的代码的缩减版本(这不是实现的代码,但足够接近它):

const char * loadedPath = "maps\\map.png"
std::string loadedPathStr = std::string( loadedPath );
auto wPath = std::filesystem::path( loadedPathStr );
wPath = wPath.make_preferred();

basePath = std::filesystem::path( "./a/b/" );
auto totalPath = basePath / wPath;
auto wStr = totalPath.generic_string();
std::cout << wStr << std::endl;

这会输出“./a/b/maps\\map.png”

在调试实现的代码时,看起来wPath 被优化了;没有办法检查它。

奇怪的是,当我编译并运行这个独立的测试程序时,它按预期工作:

int main(){
   assert( std::filesystem::path::preferred_separator == '/' );            
   const char * cPath = "maps\\map.png";
   std::string path = std::string( cPath );
   auto wPath = std::filesystem::path( path );
   wPath = wPath.make_preferred();
   std::string wStr = wPath.generic_string();
   std::cout << wStr << std::endl;
}

这会输出“maps/map.png”。我看不懂。这也会输出不正确的值。

有人知道这里发生了什么吗?

编辑: 尝试用clang编译(之前用gcc),效果如预期(分隔符被转换)。忽略这个,重新编译出错了。

我在 Linux 上运行这个,路径存在。

【问题讨论】:

  • 您使用的是 POSIX 平台还是 Windows?如果是 POSIX,在 assets 子目录 models 中有一个名为 maps\map.png 的文件吗?
  • 你的工具链是什么?
  • 请以 MCVE 形式提供非工作代码,例如 this
  • 问题可能是:在Windows上\ / 都是路径分隔符,而\ 是首选。在 POSIX 上,/ 是路径分隔符,而 \ 不是路径分隔符,因此 make_preferred 不会转换 \ (它只是一个有效的普通文件名字符)。
  • 顺便说一句,make_preferredgeneric_string 基本上都做同样的事情,所以你不应该同时使用它们。 make_preferred 用于当您希望路径的内部存储字符串使用通用分隔符时。 generic_string 用于当您想要通用格式的路径字符串的 副本 时。

标签: c++ c++17


【解决方案1】:

我是不是误解了make_preferred()的目的?

不完全是,但微妙地是的。目录分隔符(通用格式)要么是首选分隔符,要么是备用分隔符:/。在首选分隔符为/(例如POSIX)的系统上,目录分隔符仅为/。在这样的系统上make_preferred 不会修改路径,这就解释了为什么它会被完全优化。

用正斜杠替换反斜杠的最简单方法是std::replace。但是,请注意反斜杠是 POSIX 文件名中的有效字符,因此这种转换可能会破坏使用它的文件名的使用。

【讨论】:

  • 有道理,感谢您的解释。不幸的是,路径系统不会进行转换,但实际上它不会进行转换是有道理的,因为反斜杠是有效的文件名字符。
  • 这里还有一个问题,为什么独立版本可以工作?
  • 似乎我无法读取自己的终端。在那里重新检查,你是正确的,它不起作用。很抱歉!
【解决方案2】:

如果你想用filesystem 编写跨平台代码,你应该尽量坚持通用格式。所有其他格式的文件系统字符串的行为取决于实现。

允许替代目录分隔符的实现会将它们视为目录分隔符。但是其他完全有效的无法识别这些分隔符的实现将无法识别它们。 “/”始终是目录分隔符; “\”是否是分隔符取决于实现。

make_preferred 从实现的路径格式转换为通用格式。因此,它的行为取决于实现。

处理非通用格式的主要原因是当您从本机 OS API 获取路径字符串时。此类路径字符串可能采用实现的格式,因此path 需要能够识别它们并使用它们。对于程序中内置的字符串文字,您应该始终更喜欢通用格式(除非您的应用程序是特定于操作系统的,或者您正在根据使用代码的操作系统选择不同的字符串)。

【讨论】:

  • 理想情况下,我会坚持所有字符串文字的通用格式,但不幸的是,我正在处理加载具有烘焙文件路径的 3D 模型文件,而我几乎无法控制格式。不过感谢您的回复,在此和其他之间,我看到了我对 make_preferred() 函数(以及分隔符的整体处理)的误解
猜你喜欢
  • 2021-08-21
  • 2019-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-31
  • 1970-01-01
  • 2022-06-14
  • 1970-01-01
相关资源
最近更新 更多