Gibt den relativen Pfad aus

15

Beschreibung

Geben Sie unter Angabe eines Quellpfads und eines Zielpfads den relativen Pfad zum Ziel in Bezug auf die Quelle aus.

Regeln

  1. Die Eingabe kann von stdin oder als Argument für das Programm / die Funktion erfolgen.

  2. Sowohl Windows- als auch Unix-Pfade müssen unterstützt werden.

  3. Der Ausgabepfad kann /und / oder \als Pfadtrennzeichen verwendet werden (Ihre Wahl und Kombination aus beiden ist in Ordnung).

  4. Sie können davon ausgehen, dass ein relativer Pfad möglich ist.

  5. Die Verwendung externer Programme, eingebauter Funktionen oder Bibliotheksfunktionen zur Berechnung relativer Pfade ist untersagt (z. B. Pythons os.path.relpath).

  6. Das ist

    Bearbeiten: Neue Regel aus Kommentaren.

  7. Der relative Pfad muss der kürzest mögliche relative Pfad sein.

  8. Angenommen, der Zielpfad unterscheidet sich vom Quellpfad.

Beispiel 1

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

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

Beispiel 2

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

# Out
..\WindowsPowerShell\v1.0
Rynant
quelle
In Bezug auf Regel 3 - ist eine Mischung in Ordnung? Eg ../../vim\vim73\ftplugin.
Duncan Jones
1
Müssen wir den kürzesten relativen Pfad zurückgeben oder ist es in Ordnung, einen Pfad zu liefern?
Howard
@Duncan Ja, eine Mischung ist in Ordnung.
Rynant
1
@Howard, es muss der kürzeste relative Pfad sein.
Rynant
sollte nicht das erste Beispiel sein ../vim/vim73/ftplugin?
Martijn

Antworten:

2

CJam, 46 Bytes

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

Probieren Sie es online aus.

Beispiele

$ 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

Wie es funktioniert

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
quelle
1
Es hat einen Bug. Versuchen Sie es /aa/xmit /ab/y.
Jimmy23013
@ user23013: Behoben.
Dennis
2

Bash + Coreutils, 116

Hier ist ein Shell-Skript, um den Ball ins Rollen zu bringen. Ich bin mir ziemlich sicher, dass es kürzere Antworten geben wird:

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

Ausgabe:

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

Beachten Sie, dass das Skript nicht erkennen kann, ob es sich um eine Zeichenfolge handelt ftplugin eine Datei oder ein Verzeichnis ist. Sie können ein Verzeichnis explizit bereitstellen, indem Sie es /wie im obigen Beispiel mit einem anhängen .

Behandelt keine Pfade, die Leerzeichen oder andere lustige Zeichen enthalten. Ich bin mir nicht sicher, ob dies eine Anforderung ist oder nicht. Es wären nur ein paar zusätzliche Anführungszeichen erforderlich.

Digitales Trauma
quelle
2

Javascript (E6) 104

Bearbeiten Hinzugefügt Warnung für die Ausgabe

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

Ungolfed

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

Prüfung

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
quelle
2

Ruby> = 1,9, 89 94 Zeichen

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

Eingabe über Kommandozeilenargumente. Funktioniert für Pfade im UNIX- und Windows-Stil, einschließlich Pfade mit wiederholten Ordnernamen:

$ 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
quelle
2

J - 63 Zeichen

Eine Funktion, die links den alten und rechts den neuen Pfad einnimmt.

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

Diese Lösung besteht aus drei Teilen post@loop&pre~. Erklärt durch Explosion:

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

Beachten Sie, dass wir /vor dem Aufteilen jedem Pfad einen Verweis hinzufügen , damit wir Windows-artige Pfade verarbeiten, indem wir sie C:in einen "Ordner" umwandeln. Dies führt zu einem leeren Ordner am Anfang von Pfaden im Unix-Stil, der jedoch immer von der Schleife entfernt wird.

Sehen Sie es in Aktion:

   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

Sie können es auch selbst bei tryj.tk versuchen .

algorithmshark
quelle
2

Bash, 69 66

Ich habe diesen Beitrag nicht gepostet, weil ich dachte, jemand muss es viel besser können. Aber anscheinend ist es nicht so einfach.

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

NLässt sedzwei Zeilen zusammenpassen. Der erste Ausdruck entfernt das gemeinsame Präfix, das mit /oder endet \. Der zweite Ausdruck ersetzt die Verzeichnisnamen ..in der ersten Zeile durch. Schließlich werden die beiden Zeilen mit dem Trennzeichen verkettet.

Danke an Hasturkun für 3 Charaktere.

jimmy23013
quelle
Sieht interessant aus! Können Sie eine Erklärung hinzufügen?
Digital Trauma
1
@DigitalTrauma Hinzugefügt. Aber im Grunde sind sie nur reguläre Ausdrücke.
Jimmy23013
Vielen Dank! Ich werde damit spielen, wenn ich das nächste Mal an einem Terminal bin
Digital Trauma
Sie müssen nicht wirklich sedzweimal ausgeführt werden, sondern können dies mit einem einzigen Skript tun.
Hasturkun
@Hasturkun Aber ich habe keinen Weg gefunden, damit es funktioniert N. Vielleicht können Sie diese Antwort bearbeiten, wenn Sie wissen, wie.
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
quelle
p(char*s,char*d){for(;*s;)*s++-*d?*s-47||printf("../"):d++;puts(d);}68 Zeichen ohne Backslash
Bebe
Vielen Dank! Aber Regel 2 besagt, dass beide unterstützt werden müssen. In der Ausgabe kann ich den einen oder anderen auswählen (Regel 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))

Beispiel:

$ python3 path.py
 /usr/share/geany/colorschemes
/usr/share/vim/vim73/ftplugin 
../../vim/vim73/ftplugin
grc
quelle
Könnte es einen kürzeren Weg geben, um Zeile 1 mit execund String-Operationen durchzuführen?
27.
@xnor Vielleicht, aber ich kann es nicht sehen.
Grc
Könnte map(input,' ')für `(input (), input ()) funktionieren? (Ich kann es nicht selbst testen)
Xnor
@xnor Ja das funktioniert danke!
Grc
1

Rubin - 89

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

Verwendung:

ruby relative.rb working/directory destination/directory
Jwosty
quelle
3
Dies schlägt fehl für Argumente wie /foo/bar/foo/barund /foo/qux/foo/bar.
Ventero
Und scheitert für Windows-Stil Pfade
edc65
@ edc65 Die Regeln besagen nicht, dass beide Pfadformate unterstützt werden müssen.
Nderscore
@nderscore Regel 2 Sowohl Windows- als auch Unix-Pfade müssen unterstützt werden.
Edc65
1
@Jwosty: Nun, das ist die Schönheit, nicht wahr? Eine Lösung finden, die kurz und richtig ist. Ich hatte in der Vergangenheit Fälle, in denen ich die Antwort wegen eines übersehenen Randfalls komplett überarbeiten musste. In diesem Fall gebe ich die Schuld auch teilweise der Aufgabe, da ich glaube, dass eine solide Reihe von Testfällen jede Aufgabe begleiten sollte, aber gut.
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("/")}

Analysiert beide Pfadformate, gibt jedoch mit /Trennzeichen aus.

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
quelle
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);}

Ungolfed:

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
quelle
Deine Antwort ist nicht korrekt. Versuchen Sie, diese dirs und cdbilden Sie eine zu anderen :)
Core1024
0

Groovy - 144 Zeichen

Eine Lösung:

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

Beispielausgabe:

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

ungolfed:

// 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
quelle