我正在编写的代码涉及从一个路径加载文件,该路径由给定的"基本"路径和从另一个文件加载的次要相对路径串联而成。例如(在我遇到问题的地方),基本路径是"assets/models/",次要路径是"maps\map.png"。这两个字符串的直接连接给出了"assets/models/maps\map.png"。当在POSIX系统上运行时,无法加载。到目前为止,我一直在通过将反斜杠替换为的正斜杠来解决这个问题
std::replace( path.begin(), path.end(), '\', '/' );
但我想用C++17的CCD_ 2来代替。
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上运行这个,路径是存在的。
我是否误解了
make_preferred()
的用途?
不完全是,但微妙地是。目录分隔符(通用格式)是首选分隔符,也可以是后备分隔符:/
。在首选分隔符为/
(例如POSIX)的系统上,目录分隔符仅为/
。在这样的系统上,make_preferred
不会修改路径,这解释了为什么它会被完全优化。
用正斜杠替换反斜杠的最简单方法是std::replace
。但是,请注意,在POSIX中,反斜杠是文件名中的有效字符,因此这种转换可能会中断使用它的文件名的使用
如果您想用filesystem
编写跨平台代码,您应该尽量使用通用格式。所有其他格式的文件系统字符串的行为都取决于实现。
一个允许替代目录分隔符的实现将把它们当作目录分隔符。但是,其他完全有效的、不识别这些分隔符的实现将不会识别它们。"/"始终是目录分隔符;"\"是否是分隔符取决于实现。
make_preferred
从实现的路径格式转换为通用格式。因此,它的行为依赖于实现。
处理非通用格式的主要原因是从本机操作系统API获取路径字符串。这样的路径字符串可能是实现的格式,因此path
需要能够识别它们并使用它们。对于程序中内置的字符串文字,您应该始终选择通用格式(除非您的应用程序是特定于操作系统的,或者您根据代码使用的操作系统选择不同的字符串)。