Wydrukuj ścieżkę względną

15

Opis

Podając ścieżkę źródłową i ścieżkę docelową, wypisz ścieżkę względną do miejsca docelowego względem źródła.

Zasady

  1. Dane wejściowe mogą pochodzić ze stdin lub jako argumenty programu / funkcji.

  2. Ścieżki stylu Windows i Unix muszą być obsługiwane.

  3. Ścieżka wyjściowa może być użyta /i / lub \dla separatora ścieżek (twój wybór i połączenie obu jest OK).

  4. Możesz założyć, że ścieżka względna jest możliwa.

  5. Korzystanie z zewnętrznych programów, funkcji wbudowanych lub funkcji bibliotecznych przeznaczonych do obliczania ścieżek względnych jest zabronione (np. Pythona os.path.relpath)

  6. To jest

    Edycja: Nowa reguła z komentarzy.

  7. Ścieżka względna musi być możliwie najkrótszą ścieżką względną.

  8. Załóżmy, że ścieżka docelowa jest inna niż ścieżka źródłowa.

Przykład 1

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

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

Przykład 2

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

# Out
..\WindowsPowerShell\v1.0
Rynant
źródło
Odnośnie do zasady nr 3 - czy mieszanka jest w porządku? Np ../../vim\vim73\ftplugin.
Duncan Jones
1
Czy musimy zwracać najkrótszą ścieżkę względną, czy też można podać jakąkolwiek ścieżkę?
Howard
@ Duncan Tak, miks jest w porządku.
Rynant
1
@Jak to musi być najkrótsza ścieżka względna.
Rynant
nie powinien być pierwszym przykładem ../vim/vim73/ftplugin?
Martijn

Odpowiedzi:

2

CJam, 46 bajtów

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

Wypróbuj online.

Przykłady

$ 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

Jak to działa

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 “/”.                                                  ";
Dennis
źródło
1
Ma błąd. Spróbuj /aa/xz /ab/y.
jimmy23013
@ user23013: Naprawiono.
Dennis
2

Bash + coreutils, 116

Oto skrypt powłoki, aby uruchomić piłkę. Dość pewne, że będą krótsze odpowiedzi:

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

Wynik:

$ ./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
$ 

Uwaga: skrypt nie może stwierdzić, czy łańcuch ftpluginjest plikiem, czy katalogiem. Możesz jawnie podać katalog, dołączając go do/ jak w powyższym przykładzie.

Nie obsługuje ścieżek zawierających białe znaki ani innych zabawnych postaci. Nie jestem pewien, czy jest to wymóg, czy nie. Potrzebnych byłoby tylko kilka dodatkowych cytatów.

Cyfrowa trauma
źródło
2

JavaScript (E6) 104

Edytuj Dodano alert dla wyjścia

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

Nie golfił

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

Test

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

edc65
źródło
2

Rubin> = 1,9, 89 94 postacie

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

Wprowadzanie za pomocą argumentów wiersza poleceń. Działa zarówno dla ścieżek w stylu UNIX, jak i Windows, w tym ścieżek z powtarzającymi się nazwami folderów:

$ 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
Ventero
źródło
2

J - 63 znak

Funkcja pobierająca starą ścieżkę po lewej i nową ścieżkę po prawej.

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

To rozwiązanie składa się z trzech części post@loop&pre~. Wyjaśnione przez wybuch:

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

Pamiętaj, że /przed podziałem dodajemy wiodącą ścieżkę , abyśmy mogli obsługiwać ścieżki w stylu Windows, tworząc C:„folder”. Powoduje to, że folder jest pusty na początku ścieżek w stylu uniksowym, ale zawsze jest usuwany przez pętlę.

Zobacz to w akcji:

   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

Można też spróbować go samodzielnie na tryj.tk .

algorytmshark
źródło
2

Bash, 69 66

Nie opublikowałem tego, ponieważ myślałem, że ktoś musi być w stanie zrobić to znacznie lepiej. Ale najwyraźniej nie jest to takie proste.

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

Nmarki seddopasować dwie linie razem. Pierwsze wyrażenie usuwa wspólny prefiks kończący się na /lub \. Drugie wyrażenie zamienia nazwy katalogów ..na w pierwszym wierszu. Na koniec łączy dwie linie z separatorem.

Dzięki Hasturkun za 3 postacie.

jimmy23013
źródło
Wygląda ciekawie! Czy możesz dodać wyjaśnienie?
Digital Trauma
1
@DigitalTrauma Dodano. Ale w zasadzie są to tylko wyrażenia regularne.
jimmy23013
Dzięki! Będę grał następnym razem, gdy będę na terminalu
Digital Trauma
Tak naprawdę nie musisz uruchamiać się seddwukrotnie, możesz to zrobić za pomocą jednego skryptu.
Hasturkun
@Hasturkun Ale nie znalazłem sposobu, żeby to działało N. Może możesz edytować tę odpowiedź, jeśli wiesz jak.
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);}
kwokkie
źródło
p(char*s,char*d){for(;*s;)*s++-*d?*s-47||printf("../"):d++;puts(d);}68 znaków bez ukośnika odwrotnego
be
Dzięki! Ale reguła 2 mówi, że oba muszą być obsługiwane. To w wyjściu mogę wybrać jedno lub drugie (reguła 3).
kwokkie
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))

Przykład:

$ python3 path.py
 /usr/share/geany/colorschemes
/usr/share/vim/vim73/ftplugin 
../../vim/vim73/ftplugin
grc
źródło
Czy może istnieć krótszy sposób wykonywania linii 1 execi operacji na łańcuchach?
xnor
@ xnor Może, ale nie widzę tego.
grc
Czy może map(input,' ')działać dla `(input (), input ())? (Sam nie mogę tego przetestować)
xnor
@xnor Tak, to działa dzięki!
grc
1

Rubin - 89

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

Stosowanie:

ruby relative.rb working/directory destination/directory
Jwosty
źródło
3
Nie udaje się to w przypadku argumentów takich jak /foo/bar/foo/bari /foo/qux/foo/bar.
Ventero
I nie udaje się w przypadku ścieżek w stylu Windows
edc65
@ edc65 Reguły nie mówią, że konieczne jest obsługiwanie obu formatów ścieżek, możesz wykonać jeden z nich.
nderscore
@nderscore Reguła 2 Ścieżki stylu Windows i Unix muszą być obsługiwane.
edc65
1
@Jwosty: Cóż, to jest piękno, prawda? Opracowanie rozwiązania, które jest zarówno krótkie, jak i prawidłowe. W przeszłości miałem przypadki, w których musiałem całkowicie zrewidować odpowiedź z powodu przeoczonego przypadku krawędzi. Teraz w tym przypadku częściowo obwiniam również zadanie, ponieważ uważam, że solidny zestaw przypadków testowych powinien towarzyszyć każdemu zadaniu, ale dobrze.
Joey
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("/")}

Analizuje albo format ścieżki, ale dane wyjściowe z /separatorem.

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
Matt
źródło
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);}

Nie golfowany:

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
Martijn
źródło
Twoja odpowiedź jest nieprawidłowa. Spróbuj zrobić te katalogi i cd
stwórz
0

Groovy - 144 znaki

Jedno rozwiązanie:

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}"

przykładowe dane wyjściowe:

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

bez golfa:

// 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}"
Michael Easter
źródło