给出以下代码:
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main()
{
fs::path fsBase = "/base";
fs::path fsAppend = "/append";
auto fsResult = fsBase / fsAppend;
std::cout << "fsResult: " << fsResult << std::endl;
return 0;
}
通常,预期结果为/base/append
,但实际上得到的是/append
。
fs::path::append的描述确实表明了这种行为:
如果p.is_absolute()|| (p.has_root_name()&& p.root_name()!= root_name()),然后将当前路径替换为p,就像由operator =(p)一样,完成操作。
但是,std::experimental::filesystem
和boost::filesystem
的行为是不同的,这给出了预期的/base/append
。
参见examples。
问题是为什么它会这样表现?为什么用append()
函数替换路径?
答案 0 :(得分:4)
fsAppend
是绝对路径,因为它以/
开头,并且您使用的是POSIX这样的系统,其中以/
开头的路径是绝对路径。
将一个绝对路径附加到另一绝对路径没有任何意义(对我来说,抛出异常实际上是最自然的结果)。 C:\foo.txt
附加C:\bar.txt
的结果应该是什么?
在experimental::fs
中,规则是,如果第二个参数的.native()
以目录分隔符开头,则它被视为用于追加目的的相对路径,即使在其他上下文中它可能是绝对路径!
标准化文件系统清楚地将绝对路径与相对路径区分开来,从而避免了POSIX系统上出现的这种歧义。
可以在P0492R2 US77中找到更改的摘要。
请注意,您可以使用+=
而不是/
进行串联(应该做您期望的事情),或者在使用/
之前将第二个参数设为相对值。
另请参见this answer,以进一步了解experimental
和最终版本之间的比较。
答案 1 :(得分:1)
文件名不是(道德上的)字符串:添加路径 a 和相对路径 b 结构化可以回答问题
如果 a 是当前目录,那么路径 b 是什么意思?
首先,如果 a 是当前的工作目录,则这是相对的→绝对功能(尽管filesystem::absolute
会执行因为Windows D:foo
既不是相对的,也不是绝对的。
例如,考虑#include"…"
的行为:如果文件名是相对的,则首先考虑从包含#include
的文件所在的目录开始,然后考虑从每个目录开始包含路径的元素( eg ,-I/start/here
)。每个问题都可以表达为问上述问题:
void handle_include(const std::filesystem::path &reading,
const std::filesystem::path &name,
const std::vector<std::filesystem::path> &srch) {
std::ifstream in(reading.parent_path()/name);
if(!in) {
for(auto &s : srch) {
in.open(s/name);
if(in) break;
}
if(!in) throw …;
}
// use in
}
如果name
是绝对(例如 ,#include"/usr/include/sys/stat.h"
),应该怎么办?唯一正确的答案是使用name
而不考虑reading
或s
。 (在这里,这将多次低效地考虑同一个文件,但这是效率问题,而不是正确性,并且仅影响错误情况。)还请注意a/b.lexically_proximate(a)==b
的相关身份; lexically_proximate
可以返回绝对路径(当两个路径具有不同的根名称时),而lexically_relative
可以失败并丢失信息。
这种方法还避免了在 Windows 上盲目级联给出的不必要的无用答案:C:\foo\D:\bar
甚至不是有效的文件名,更不用说任何人本可以通过以下方式获得的文件名了:结合其碎片。当然,提出例外也可以避免这种情况,但是以防止上述合理的使用案例为代价。甚至有path("c:\\foo\\bar").append("\\baz\\quux")
的情况,它保留每个元素的部分,并产生path("c:\\baz\\quux")
,这再次是上述问题的正确答案。
鉴于没有人应该写类似的东西
[project]
headers=/include
manual=/doc
在这种解释不正确的情况下,没有理由将右手操作数设为绝对值。 (很明显,如果可以的话,可以写base/cfg.relative_path()
;这就是the follow-on question in a comment的答案。)
此行为的灵感来自Python的os.path.join
,它依次对每个参数都执行了此操作。