如何找到两条绝对路径的相对路径?

时间:2015-03-14 23:34:00

标签: c path relative-path absolute-path

给出两条绝对路径,例如

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

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

从某种意义上说,与realpath所做的相反。

5 个答案:

答案 0 :(得分:6)

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

这没有标准功能。为此,vi-like-emacs中有一个函数。快速检查apropos relative向我展示了其他可能实现此目的的程序:revpath例如。)

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

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

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

答案 1 :(得分:6)

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

  • /a
  • /somewhere/else

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

../../a

如果你想走另一条路,你可以改为:

../somewhere/else

答案 2 :(得分:3)

使用第一个绝对路径构建树,然后将第二个路径添加到该树,然后从一个叶子走到另一个叶子:从一个节点到其父节点的步骤被转换为" ../ "序列,从节点到其子节点之间的步骤被转换为该子节点的名称。请注意,可能有多个解决方案。例如:

1)/a/path/to/a

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

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

答案 3 :(得分:0)

这是在GNU Coreutils包(对于ln -r)的一部分“ ln”命令中实现的。显然,有很多方法可以解决此问题,甚至可以在不查看现有代码的情况下从自己提出的解决方案中受益。我个人认为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()。多数时候,这可能就是您想要拨打的电话。

答案 4 :(得分:0)

使用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