【问题标题】:How to find relative path given two absolute paths?如何在给定两个绝对路径的情况下找到相对路径?
【发布时间】:2015-05-17 07:25:14
【问题描述】:

给定两个绝对路径,例如

  • /a/path/to/a
  • /a/path/to/somewhere/else

如何获得从一个到另一个的相对路径 ../a

在某种意义上,与realpath 的作用相反。

【问题讨论】:

    标签: c path relative-path absolute-path


    【解决方案1】:

    使用cwalk,您可以使用cwk_path_get_relative,它甚至可以跨平台工作:

    #include <cwalk.h>
    #include <stdio.h>
    #include <stddef.h>
    #include <stdlib.h>
    
    int main(int argc, char *argv[])
    {
      char buffer[FILENAME_MAX];
    
      cwk_path_get_relative("/hello/there/", "/hello/world", buffer, sizeof(buffer));
      printf("The relative path is: %s", buffer);
    
      return EXIT_SUCCESS;
    }
    

    输出:

    The relative path is: ../world
    

    【讨论】:

      【解决方案2】:

      这是在“ln”命令中实现的,它是GNU Coreutils 包的一部分(用于ln -r)。显然有很多方法可以解决这个问题,甚至可以从自己提出解决方案中获得一些好处,而无需查看现有代码。我个人觉得 Coreutils 中的代码很有启发性。

      如果我必须在 C 项目中将绝对路径转换为相对路径,我只需从 Coreutils 复制“relpath.c”。它与包中的其他实用程序功能有一些依赖关系,这些也需要以某种形式引入。这是主要的“relpath()”函数。请注意,它适用于规范化的路径名,例如,它不喜欢路径包含“//”或“/.”之类的内容。基本上它找到两条路径的公共前缀,然后根据每条路径的剩余部分是否为空来处理出现的四种情况。

      /* Output the relative representation if possible.
         If BUF is non-NULL, write to that buffer rather than to stdout.  */
      bool
      relpath (const char *can_fname, const char *can_reldir, char *buf, size_t len)
      {
        bool buf_err = false;
      
        /* Skip the prefix common to --relative-to and path.  */
        int common_index = path_common_prefix (can_reldir, can_fname);
        if (!common_index)
          return false;
      
        const char *relto_suffix = can_reldir + common_index;
        const char *fname_suffix = can_fname + common_index;
      
        /* Skip over extraneous '/'.  */
        if (*relto_suffix == '/')
          relto_suffix++;
        if (*fname_suffix == '/')
          fname_suffix++;
      
        /* Replace remaining components of --relative-to with '..', to get
           to a common directory.  Then output the remainder of fname.  */
        if (*relto_suffix)
          {
            buf_err |= buffer_or_output ("..", &buf, &len);
            for (; *relto_suffix; ++relto_suffix)
              {
                if (*relto_suffix == '/')
                  buf_err |= buffer_or_output ("/..", &buf, &len);
              }
      
            if (*fname_suffix)
              {
                buf_err |= buffer_or_output ("/", &buf, &len);
                buf_err |= buffer_or_output (fname_suffix, &buf, &len);
              }
          }
        else
          {
              buf_err |= buffer_or_output (*fname_suffix ? fname_suffix : ".",
                                           &buf, &len);
          }
      
        if (buf_err)
          error (0, ENAMETOOLONG, "%s", _("generating relative path"));
      
        return !buf_err;
      }
      

      您可以查看“relpath.c”here 的其余部分。 “ln.c”中有一个更高级别的包装函数,它在调用 relpath 之前规范化它的路径名参数,它被命名为convert_abs_rel()。这可能是您大部分时间想要调用的内容。

      【讨论】:

        【解决方案3】:

        找到最长的公共路径(在本例中为/a/path/to)并将其从两个绝对路径中删除。那将给出:

        • /a
        • /somewhere/else

        现在,用../ 替换起始路径中的每个路径组件,并将结果添加到目标路径。如果你想从目录else 转到目录a,那会给你:

        ../../a
        

        如果你想走另一条路,你应该有:

        ../somewhere/else
        

        【讨论】:

          【解决方案4】:

          我在这里回答了类似的问题:Resolving a relative path without referencing the current directory on Windows

          对此没有标准功能。为此目的,在 vi-like-emacs 中有一个功能。对apropos relative 的快速检查显示了一些其他可能实现此功能的程序:例如revpath

          它可以作为字符串操作完成(无需计算工作目录):

          • 首先查找以路径分隔符结尾的最长公共前缀。
          • 如果没有公共前缀,你就完了
          • 从当前和目标字符串(...的副本)中去除公共前缀
          • 将当前字符串中的每个目录名替换为“..”
          • 在目标字符串前面添加(使用路径分隔符)
          • 返回组合字符串

          第二步中的“完成”假定你想使用相对路径来缩短结果。另一方面,您可能希望使用相对路径名而不管长度如何。在这种情况下,只需跳过这一步(结果会更长,但相对)。

          【讨论】:

            【解决方案5】:

            用第一个绝对路径构建一棵树,然后将第二个路径添加到该树,然后从一个叶子走到另一个叶子:从一个节点到其父节点的步骤被转换为“../”序列,从一个节点到它的一个子节点的一步被翻译成那个孩子的名字。请注意,可能有不止一种解决方案。例如:

            1) /a/path/to/a

            2) /a/path/to/a/new/one

            从 (1) 到 (2) 的明显路径是 new/one,但 ../../../a/path/to/a/new/one 也是有效的。当您编写算法在树中行走时,您必须意识到这一点

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-05-09
              • 1970-01-01
              • 2011-08-11
              • 2013-10-27
              • 1970-01-01
              • 2014-03-16
              相关资源
              最近更新 更多