打印相对路径


15

描述

给定一个源路径和一个目标路径,输出相对于源到目标的相对路径。

规则

  1. 输入可以来自stdin或作为程序/函数的参数。

  2. Windows和Unix样式路径都必须受支持。

  3. 输出路径可以使用/和/或\用作路径分隔符(您可以选择和两者组合使用)。

  4. 您可以假设相对路径是可能的。

  5. 禁止使用用于计算相对路径的外部程序,内置函数或库函数(例如Python的os.path.relpath

  6. 这是

    编辑:评论中的新规则。

  7. 相对路径必须是可能的最短相对路径。

  8. 假设目标路径与源路径不同。

例子1

# In
/usr/share/geany/colorschemes
/usr/share/vim/vim73/ftplugin

# Out
../../vim/vim73/ftplugin

例子2

# In
C:\Windows\System32\drivers
C:\Windows\System32\WindowsPowerShell\v1.0

# Out
..\WindowsPowerShell\v1.0

关于规则3-混合可以吗?例如../../vim\vim73\ftplugin
邓肯·琼斯

1
我们必须返回最短的相对路径还是可以产生任何路径?
2014年

@Duncan是的,混合还可以。
雷纳特2014年

1
@Howard,它必须是最短的相对路径。
雷纳特2014年

第一个例子不应该../vim/vim73/ftplugin吗?
Martijn 2014年

Answers:


2

CJam,46个字节

ll]{'\/'/f/:~}/W{)__3$=4$@==}g@,1$-"../"*o>'/*

在线尝试。

例子

$ echo '/usr/share/geany/colorschemes
> /usr/share/vim/vim73/ftplugin' | cjam path.cjam; echo
../../vim/vim73/ftplugin
$ echo 'C:\Windows\System32\drivers
> C:\Windows\System32\WindowsPowerShell\v1.0' | cjam path.cjam; echo
../WindowsPowerShell/v1.0

怎么运行的

ll]         " Read two lines from STDIN and wrap them in an array.                       ";
{           " For each line:                                                             ";
  '\/       " Split by “\”.                                                              ";
  '/f/      " Split each chunk by “/”.                                                   ";
  :~        " Flatten the array of chunks.                                               ";
}/          "                                                                            ";
W           " Push -1 (accumulator).                                                     ";
{           "                                                                            ";
  )__       " Increment and duplicate twice.                                             ";
  3$=       " Extract the corresponding chunk from the first line.                       ";
  4$@=      " Extract the corresponding chunk from the second line.                      ";
  =         " If the are equal,                                                          ";
}g          " repeat the loop.                                                           ";
@,          " Rotate the array of chunks of the first line on top and get its length.    ";
1$-         " Subtract the value of the accumulator.                                     ";
"../"*o     " Print the string “../” repeated that many times.                           ";
>           " Remove all chunks with index less than the accumulator of the second line. ";
'/*         " Join the chunks with “/”.                                                  ";

1
它有一个错误。尝试/aa/x/ab/y
jimmy23013 2014年

@ user23013:固定。
丹尼斯

2

Bash + coreutils,116

这是一个使脚本运转的shell脚本。可以肯定,答案会更短:

n=`cmp <(echo $1) <(echo $2)|grep -Po "\d+(?=,)"`
printf -vs %`grep -o /<<<${1:n-1}|wc -l`s
echo ${s// /../}${2:n-1}

输出:

$ ./rel.sh /usr/share/geany/colorschemes /usr/share/vim/vim73/ftplugin
../vim/vim73/ftplugin
$ ./rel.sh /usr/share/geany/colorschemes/ /usr/share/vim/vim73/ftplugin/
../../vim/vim73/ftplugin/
$ ./rel.sh /usr/share/vim/vim73/ftplugin /usr/share/geany/colorschemes
../../geany/colorschemes
$ 

请注意,脚本无法分辨字符串ftplugin是文件还是目录。/如上例所示,您可以通过在目录后附加一个来显式提供目录。

无法处理包含空格或其他有趣字符的路径。不知道这是否是必需的。仅需要一些额外的报价。


2

Javascript(E6)104

编辑为输出添加的警报

R=(s,d)=>alert(s.split(x=/\/|\\/).map(a=>a==d[0]?d.shift()&&'':'../',d=d.split(x)).join('')+d.join('/'))

不打高尔夫球

R (s,d) => // a single espression is returned, no {} or () needed
  s.split(x=/\/|\\/) // split string at / or \, save regexp in X for later
  .map( // create a new array from s
     a => a == d[0] // check if current of s and d equals
          ? d.shift() && '' // map to '' and cut 1 element of d
          : '../', // else map to '../'
     d=d.split(x)) // second param of map is useless, so split d here
  .join('')+d.join('/') // join map and concat to rest of d adding separators

测试

R('C:\\Windows\\System32\\drivers','C:\\Windows\\System32\\WindowsPowerShell\\v1.0')

../WindowsPowerShell/v1.0

R('/usr/share/geany/colorschemes','/usr/share/vim/vim73/ftplugin')

../../vim/vim73/ftplugin


2

Ruby> = 1.9、89 94 人物

$;=/\\|\//
a,b=$*.map &:split
puts"../"*(a.size-r=a.index{a[$.+=1]!=b[$.]}+1)+b[r..-1]*?/

通过命令行参数输入。适用于UNIX和Windows风格的路径,包括具有重复文件夹名称的路径:

$ ruby relpath.rb /usr/share/geany/colorschemes /usr/share/vim/vim73/ftplugin
../../vim/vim73/ftplugin
$ ruby relpath.rb 'C:\Windows\System32\drivers' 'C:\Windows\System32\WindowsPowerShell\v1.0'
../WindowsPowerShell/v1.0
$ ruby relpath.rb /foo/bar/foo/bar /foo/qux/foo/bar
../../../qux/foo/bar

2

J-63字符

该函数采用旧路径在左侧,新路径在右侧。

}.@;@(c=.c&}.`(,~(<'/..')"0)@.(~:&{.))&('/'<;.1@,'\/'&charsub)~

该解决方案分为三个部分,看起来像post@loop&pre~。爆炸解释:

post @ loop & pre ~   NB. the full golf
                  ~   NB. swap the arguments: new on left, old on right
            & pre     NB. apply pre to each argument
       loop           NB. run the recursive loop on both
post @                NB. apply post to the final result

'/'<;.1@,'\/'&charsub  NB. pre
         '\/'&charsub  NB. replace every \ char with /
'/'     ,              NB. prepend a / char
   <;.1@               NB. split string on the first char (/)

c=.c&}.`(,~(<'/..')"0)@.(~:&{.)  NB. loop
                      @.(~:&{.)  NB. if the top folders match:
    &}.                          NB.   chop off the top folders
   c                             NB.   recurse
       `                         NB. else:
           (<'/..')"0            NB.   change remaining old folders to /..
         ,~                      NB.   append to front of remaining new folders
c=.                              NB. call this loop c to recurse later

}.@;  NB. post
   ;  NB. turn the list of folders into a string
}.@   NB. chop off the / in the front

请注意,/在拆分之前,我们在每个路径上都添加了一个前导,以便我们通过制作C:一个“文件夹”来处理Windows样式的路径。这将导致在Unix样式路径的开始处出现一个空文件夹,但始终会被循环删除。

实际观看:

   NB. you can use it without a name if you want, we will for brevity
   relpath =. }.@;@(c=.c&}.`(,~(<'/..')"0)@.(~:&{.))&('/'<;.1@,'\/'&charsub)~
   '/usr/share/geany/colorschemes' relpath '/usr/share/vim/vim73/ftplugin'
../../vim/vim73/ftplugin
   'C:\Windows\System32\drivers' relpath 'C:\Windows\System32\WindowsPowerShell\v1.0'
../WindowsPowerShell/v1.0

您也可以在tryj.tk上尝试一下。


2

巴什69 66

我之所以没有发布此邮件,是因为我认为有人必须能够做得更好。但是显然这并不容易。

sed -r 'N;s/(.*[/\])(.*)\n\1/\2\n/'|sed '1s/[^/\]*/../g;N;s!\n!/!'

N使sed两条线匹配在一起。第一个表达式删除以/或结尾的公共前缀\。第二个表达式..在第一行中用替换目录名称。最后,它将两行与分隔符连接起来。

感谢Hasturkun提供了3个字符。


看起来很有趣!你能补充说明吗?
Digital Trauma 2014年

1
@DigitalTrauma添加了。但基本上它们只是正则表达式。
jimmy23013 2014年

谢谢!下一次我要在航站楼玩这个游戏
Digital Trauma 2014年

您实际上不需要运行sed两次,只需使用一个脚本即可执行此操作。
Hasturkun 2014年

@Hasturkun但是我找不到与之配合使用的方法N。如果您知道怎么做,也许您可​​以编辑此答案。
jimmy23013

1

C,119 106

void p(char*s,char* d){while(*s==*d){s++;d++;}s--;while(*s){if(*s==47||*s==92)printf("../");s++;}puts(d);}

p(char*s,char*d){for(;*s;)*s++-*d?*s-47||printf("../"):d++;puts(d);}68个字符
不含

谢谢!但规则2指出,两者都必须得到支持。在输出中,我可以选择一个(另一个)(规则3)。
kwokkie 2014年

1

Python 3、120

a,b=(i.split('\\/'['/'in i])for i in map(input,'  '))
while[]<a[:1]==b[:1]:del a[0],b[0]
print('../'*len(a)+'/'.join(b))

例:

$ python3 path.py
 /usr/share/geany/colorschemes
/usr/share/vim/vim73/ftplugin 
../../vim/vim73/ftplugin

也许有一种更短的方法可以使用execand字符串操作进行第1行?
xnor 2014年

@xnor也许,但是我看不到。
grc 2014年

可以map(input,' ')为`(input(),input())工作吗?(我自己无法测试)
xnor14年

@xnor是的,谢谢!
2014年

1

红宝石-89

r=/\/|\\/
s = ARGV[0].split r
d = ARGV[1].split r
puts ("../"*(s-d).size)+((d-s).join"/")

用法:

ruby relative.rb working/directory destination/directory

3
对于/foo/bar/foo/bar和这样的参数,此操作将失败/foo/qux/foo/bar
Ventero 2014年

Windows样式路径失败
-edc65

@ edc65规则没有说必须同时支持两种路径格式,您可以选择其中一种。
nderscore 2014年

@nderscore规则2必须同时支持Windows和Unix样式路径。
edc65 2014年

1
@Jwosty:好吧,那是美丽,不是吗?提出一个既简短正确的解决方案。在过去的案例中,由于被忽视的边缘案例,我不得不完全修改答案。现在,在这种情况下,我也确实将部分责任归咎于该任务,因为我认为,一组可靠的测试用例应该伴随每项任务,但是很好。
2014年

0

JavaScript-155

function p(s,d){s=s.split(/\\|\//g);d=d.split(/\\|\//g);o=[];for(i=0;s[i]==d[i];i++);for(j=s.length-i;j--;)o[j]="..";return o.concat(d.slice(i)).join("/")}

解析任一路径格式,但使用/分隔符输出。

console.log(p("/usr/share/geany/colorschemes","/usr/share/vim/vim73/ftplugin"));
../../vim/vim73/ftplugin
console.log(p("/usr/share/geany/colorschemes/test/this","/usr/share/vim/vim73/ftplugin/this/is/a/test"));
../../../../vim/vim73/ftplugin/this/is/a/test
console.log(p("c:\\windows\\system32\\drivers\\etc\\host","c:\\windows\\system\\drivers\\etc\host"));
../../../../system/drivers/etchost

0

PHP,158 151

function r($a,$b){$a=explode('/',$a);$b=explode('/',$b);foreach($a as $k=>$v){if($v==$b[$k])$b[$k]='..';else break;}unset($b[0]);echo implode('/',$b);}

取消高尔夫:

function r($a,$b){
    $a=explode('/',$a);
    $b=explode('/',$b);
    foreach($a as $k=>$v){
        if($v==$b[$k])$b[$k]='..';
        else break; 
    }
    unset($b[0]);
    echo implode('/',$b);
}
// these lines are not included in count:
r('/test/test2/abc','/test/test3/abcd'); // ../test3/abcd
r('/test/test2/abc','/test/test2/abcd'); // ../../abcd

您的答案不正确。尝试使这个目录并cd彼此形成一个:)
core1024

0

Groovy-144个字符

一种解决方案:

x=args[0][1]!=':'?'/':'\\'
f={args[it].tokenize x}
s=f 0
d=f 1
n=0
while(s[n]==d[n++]);
u="..$x"*(s.size-(--n))
println "$u${d.drop(n).join x}"

示例输出:

bash$ groovy P.groovy C:\\Windows\\System32\\drivers C:\\Windows\\System32\\WindowsPowerShell\\v1.0
..\WindowsPowerShell\v1.0

bash$ groovy P.groovy /usr/share/geany/colorschemes /usr/share/vim/vim73/ftplugin
../../vim/vim73/ftplugin

bash$ groovy P.groovy /foo/bar/foo/bar /foo/qux/foo/bar
../../../qux/foo/bar

松开

// fs = file seperator, / or \
fs = args[0][1]!=':'?'/':'\\'

s = args[0].tokenize fs
d = args[1].tokenize fs

// n = # of matching dirs from root + 1
n = 0
while (s[n] == d[n++]) ;

// up = the up-prefix. e.g. "../../..", for instance 
n--
up = "..${fs}" * (s.size-n)

println "$up${d.drop(n).join fs}"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.