如果p以根路径开头,为什么std::filesystem::path::append会替换当前路径
Why does std::filesystem::path::append replace the current path if p starts with root path
给出以下代码:
#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 的描述确实表明了这种行为:
If p.is_absolute() || (p.has_root_name() && p.root_name() != root_name()), then replaces the current path with p as if by operator=(p) and finishes.
然而,std::experimental::filesystem
和 boost::filesystem
的行为不同,这给出了预期的 /base/append
。
参见 examples。
问题是为什么会这样?为什么用append()
函数替换路径?
fsAppend
是绝对路径,因为它以 /
开头,并且您在 POSIX 等系统上,其中以 /
开头的路径是绝对路径。
将一个绝对路径附加到另一个绝对路径没有任何意义(对我来说抛出异常实际上是最自然的结果)。 C:\foo.txt
append C:\bar.txt
的结果应该是什么?
在 experimental::fs
中,规则是如果第二个参数的 .native()
以目录分隔符开头,那么它会被视为附加目的的相对路径,即使它可能是绝对路径其他情况!
标准化的文件系统明确区分了绝对路径和相对路径,试图避免在 POSIX 系统上出现的这种歧义。
可以在 P0492R2 US77 中找到更改的记录。
请注意,您可以使用 +=
而不是 /
进行连接(应该如您所愿),或者在使用 /
.[=23= 之前使第二个参数成为相对的]
另请参阅 以进一步比较 experimental
和最终确定。
文件名不是(道德上的)字符串:在结构上附加路径 a 和相对路径 b 回答问题
If a were the current directory, what would the path b mean?
首先,如果a 是当前工作目录,这是相对的→绝对 函数(虽然 filesystem::absolute
做的多一点,因为 Windows D:foo
既不完全相对也不完全绝对)。
例如考虑#include"…"
的行为:如果文件名是相对的,则首先考虑从包含带有#include
的文件的目录开始,然后考虑从来自包含路径的每个元素(例如、-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
。 (在这里,这会低效地多次考虑同一个文件,但这是效率问题,而不是正确性问题,并且只影响错误情况。)还要注意相关的 identity that a/b.lexically_proximate(a)==b
; lexically_proximate
可以 return 绝对路径(当两个路径具有不同的根名称时),而 lexically_relative
可能会失败并丢失信息。
这种方法还避免了盲目连接在 Windows 上给出的毫无意义的无用答案:C:\foo\D:\bar
甚至不是一个有效的文件名,更不用说了一个任何人都可以通过组合它的部分来获得的东西。当然引发 exception 也可以避免这种情况,但是以阻止上述合理用例为代价。甚至还有 path("c:\foo\bar").append("\baz\quux")
的情况,它保留每个的 部分 并产生 path("c:\baz\quux")
,这也是上述问题的正确答案。
鉴于没有人应该写这样的东西
[project]
headers=/include
manual=/doc
当这种解释不正确时,右侧操作数没有理由是绝对的。 (显然,如果是,可以写 base/cfg.relative_path()
;这是 的答案。)
该行为的灵感来自 Python 的 os.path.join
,它依次对每个参数执行此操作。
给出以下代码:
#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 的描述确实表明了这种行为:
If p.is_absolute() || (p.has_root_name() && p.root_name() != root_name()), then replaces the current path with p as if by operator=(p) and finishes.
然而,std::experimental::filesystem
和 boost::filesystem
的行为不同,这给出了预期的 /base/append
。
参见 examples。
问题是为什么会这样?为什么用append()
函数替换路径?
fsAppend
是绝对路径,因为它以 /
开头,并且您在 POSIX 等系统上,其中以 /
开头的路径是绝对路径。
将一个绝对路径附加到另一个绝对路径没有任何意义(对我来说抛出异常实际上是最自然的结果)。 C:\foo.txt
append C:\bar.txt
的结果应该是什么?
在 experimental::fs
中,规则是如果第二个参数的 .native()
以目录分隔符开头,那么它会被视为附加目的的相对路径,即使它可能是绝对路径其他情况!
标准化的文件系统明确区分了绝对路径和相对路径,试图避免在 POSIX 系统上出现的这种歧义。
可以在 P0492R2 US77 中找到更改的记录。
请注意,您可以使用 +=
而不是 /
进行连接(应该如您所愿),或者在使用 /
.[=23= 之前使第二个参数成为相对的]
另请参阅 experimental
和最终确定。
文件名不是(道德上的)字符串:在结构上附加路径 a 和相对路径 b 回答问题
If a were the current directory, what would the path b mean?
首先,如果a 是当前工作目录,这是相对的→绝对 函数(虽然 filesystem::absolute
做的多一点,因为 Windows D:foo
既不完全相对也不完全绝对)。
例如考虑#include"…"
的行为:如果文件名是相对的,则首先考虑从包含带有#include
的文件的目录开始,然后考虑从来自包含路径的每个元素(例如、-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
。 (在这里,这会低效地多次考虑同一个文件,但这是效率问题,而不是正确性问题,并且只影响错误情况。)还要注意相关的 identity that a/b.lexically_proximate(a)==b
; lexically_proximate
可以 return 绝对路径(当两个路径具有不同的根名称时),而 lexically_relative
可能会失败并丢失信息。
这种方法还避免了盲目连接在 Windows 上给出的毫无意义的无用答案:C:\foo\D:\bar
甚至不是一个有效的文件名,更不用说了一个任何人都可以通过组合它的部分来获得的东西。当然引发 exception 也可以避免这种情况,但是以阻止上述合理用例为代价。甚至还有 path("c:\foo\bar").append("\baz\quux")
的情况,它保留每个的 部分 并产生 path("c:\baz\quux")
,这也是上述问题的正确答案。
鉴于没有人应该写这样的东西
[project]
headers=/include
manual=/doc
当这种解释不正确时,右侧操作数没有理由是绝对的。 (显然,如果是,可以写 base/cfg.relative_path()
;这是
该行为的灵感来自 Python 的 os.path.join
,它依次对每个参数执行此操作。