给定两条绝对路径,如何表达相对于另一条路径的路径?

Ale*_*lec 4 path rust

我认为这应该是非常可行的,因为有一个很好的函数canonicalize可以规范化路径(所以我可以从规范化我的两个输入路径开始)PathPathBuf给我们一种迭代路径部分的方法components.我想在这里可以解决一些公共前缀,然后..在锚路径中保留尽可能多的组件,以保留初始输入路径的剩余部分.

我的问题似乎很常见:

  1. 如何找到两条绝对路径的相对路径?
  2. 在Windows中查找相对于另一个的路径

ken*_*ytm 11

如果一个路径是另一个路径的基础,你可以使用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(()))
Run Code Online (Sandbox Code Playgroud)

以前的化身strip_prefixpath_relative_from用来添加的../,但由于符号链接,这种行为被删除了:

  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())
    }
}
Run Code Online (Sandbox Code Playgroud)


Man*_*rth 7

这现在作为pathdiff箱子存在,使用来自kennytm答案的代码

您可以将其用作:

extern crate pathdiff;

pathdiff::diff_paths(path, base);
Run Code Online (Sandbox Code Playgroud)

其中base是其中相对路径应适用于获得path