如果p以根路径开头,为什么std :: filesystem :: path :: append替换当前路径

时间:2019-03-18 02:52:34

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

给出以下代码:

#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::filesystemboost::filesystem的行为是不同的,这给出了预期的/base/append。 参见examples

问题是为什么它会这样表现?为什么用append()函数替换路径?

2 个答案:

答案 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 而不考虑readings。 (在这里,这将多次低效地考虑同一个文件,但这是效率问题,而不是正确性,并且仅影响错误情况。)还请注意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,它依次对每个参数都执行了此操作。