给定两个绝对路径,如何将其中一个路径表示为相对于另一个路径的路径?

24

我认为这应该是很容易做到的,因为有一个好用的函数canonicalize可以规范化路径(所以我可以通过规范化我的两个输入路径来开始),PathPathBuf可以通过components提供一种迭代路径部件的方法。我想在这里可以想出一些方法来分解公共前缀,然后将锚路径中剩余的每个 .. 部件添加到初始输入路径的剩余部分。

我的问题似乎相当普遍:

  1. 如何在给定两个绝对路径的情况下找到相对路径?
  2. 在Windows中找到相对于另一个路径的路径
2个回答

28

现在这段代码已经作为pathdiff包存在,使用的是来自kennytm回答中的代码

你可以这样使用它:

extern crate pathdiff;

pathdiff::diff_paths(path, base);

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


25
如果一个路径是另一个路径的基础路径,你可以使用 Path::strip_prefix,但它不会为你计算 ../(而是返回一个错误):
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(()))
< p >之前的 < code >strip_prefix 版本是 < code >path_relative_from,它曾经添加 < code >../,但由于符号链接 此行为已被取消,现在有了新的行为: < blockquote > < ol >
  • 当前行为中,将结果与第一个路径连接起来可以明确地引用第二个路径所表示的内容,即使存在符号链接(这基本上意味着 < code >base 必须是 < code >self 的前缀)
  • 旧行为中,结果可以以 < code >../ 组件开头。符号链接意味着遍历 < code >base 路径,然后遍历返回的相对路径可能无法将您放置在遍历 < code >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())
        }
    }
    

  • 这个现在已经被提取到 pathdiff crate 中了。https://docs.rs/pathdiff/0.1.0/pathdiff/ - Manishearth
    @Manishearth 谢谢。也许你应该把它发表为一个答案 :D - kennytm

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接