【问题标题】:Given two absolute paths, how can I express one of the paths relative to the other?给定两条绝对路径,我如何表达其中一条相对于另一条的路径?
【发布时间】:2022-01-22 03:36:56
【问题描述】:

我认为这应该是非常可行的,因为有一个很好的函数 canonicalize 可以规范化路径(所以我可以从规范化我的两个输入路径开始)和 PathPathBuf 给我们一种迭代的方式通过components 的部分路径。我想可以在这里解决一些问题以分解出一个公共前缀,然后将锚路径中剩余的.. 组件添加到初始输入路径的剩余部分。

我的问题似乎很常见:

  1. How to find relative path given two absolute paths?
  2. Find a path in Windows relative to another

【问题讨论】:

    标签: path rust


    【解决方案1】:

    这现在以pathdiff crate 的形式存在,使用来自kennytm's answer 的代码

    您可以将其用作:

    extern crate pathdiff;
    
    pathdiff::diff_paths(path, base);
    

    其中base 是应用相对路径以获取path 的位置

    【讨论】:

      【解决方案2】:

      如果一个路径是另一个路径的基础,您可以使用 Path::strip_prefix,但它不会为您计算 ../(而是返回一个 Err):

      use std::path::*;
      let base = Path::new("/foo/bar");
      let child_a = Path::new("/foo/bar/a");
      let child_b = Path::new("/foo/bar/b");
      println!("{:?}", child_a.strip_prefix(base));     // Ok("a")
      println!("{:?}", child_a.strip_prefix(child_b));  // Err(StripPrefixError(()))
      

      strip_prefix的前身是path_relative_from,以前加了../,但是this behavior was dropped due to symlinks

      1. 当前将结果连接到第一条路径的行为明确指的是第二条路径所做的相同事情,即使有符号链接(这基本上意味着base 需要是self 的前缀)
      2. 结果可以以../ 组件开头的旧行为。符号链接意味着遍历base 路径,然后遍历返回的相对路径可能不会将您置于与遍历self 路径相同的目录中。但是,当您使用不关心符号链接的基于路径的系统,或者您已经解析了正在使用的路径中的符号链接时,此操作很有用。

      如果您需要../ 行为,您可以从librustc_back(编译器后端)复制实现。我还没有在 crates.io 上找到任何提供此功能的包。

      // This routine is adapted from the *old* Path's `path_relative_from`
      // function, which works differently from the new `relative_from` function.
      // In particular, this handles the case on unix where both paths are
      // absolute but with only the root as the common directory.
      fn path_relative_from(path: &Path, base: &Path) -> Option<PathBuf> {
          use std::path::Component;
      
          if path.is_absolute() != base.is_absolute() {
              if path.is_absolute() {
                  Some(PathBuf::from(path))
              } else {
                  None
              }
          } else {
              let mut ita = path.components();
              let mut itb = base.components();
              let mut comps: Vec<Component> = vec![];
              loop {
                  match (ita.next(), itb.next()) {
                      (None, None) => break,
                      (Some(a), None) => {
                          comps.push(a);
                          comps.extend(ita.by_ref());
                          break;
                      }
                      (None, _) => comps.push(Component::ParentDir),
                      (Some(a), Some(b)) if comps.is_empty() && a == b => (),
                      (Some(a), Some(b)) if b == Component::CurDir => comps.push(a),
                      (Some(_), Some(b)) if b == Component::ParentDir => return None,
                      (Some(a), Some(_)) => {
                          comps.push(Component::ParentDir);
                          for _ in itb {
                              comps.push(Component::ParentDir);
                          }
                          comps.push(a);
                          comps.extend(ita.by_ref());
                          break;
                      }
                  }
              }
              Some(comps.iter().map(|c| c.as_os_str()).collect())
          }
      }
      

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-26
      • 1970-01-01
      • 1970-01-01
      • 2022-01-08
      相关资源
      最近更新 更多