Imprimer le chemin relatif

15

La description

Étant donné un chemin source et un chemin de destination, sortez le chemin relatif vers la destination par rapport à la source.

Règles

  1. L'entrée peut provenir de stdin ou comme arguments du programme / fonction.

  2. Les chemins de style Windows et Unix doivent être pris en charge.

  3. Le chemin de sortie peut utiliser /et / ou \pour le séparateur de chemin (votre choix et la combinaison des deux est OK).

  4. Vous pouvez supposer qu'un chemin relatif est possible.

  5. L'utilisation de programmes externes, de fonctions intégrées ou de bibliothèque conçues pour calculer des chemins relatifs est interdite (par exemple Python os.path.relpath)

  6. C'est du

    Modifier: nouvelle règle à partir des commentaires.

  7. Le chemin relatif doit être le chemin relatif le plus court possible.

  8. Supposons que le chemin de destination soit différent du chemin source.

Exemple 1

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

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

Exemple 2

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

# Out
..\WindowsPowerShell\v1.0
Rynant
la source
Concernant la règle # 3 - un mélange est-il correct? Par exemple ../../vim\vim73\ftplugin.
Duncan Jones
1
Devons-nous renvoyer le chemin relatif le plus court ou est-il acceptable de fournir un chemin?
Howard
@Duncan Oui, un mix est correct.
Rynant
1
@Howard, ce doit être le chemin relatif le plus court.
Rynant
le premier exemple ne devrait-il pas être ../vim/vim73/ftplugin?
Martijn

Réponses:

2

CJam, 46 octets

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

Essayez-le en ligne.

Exemples

$ 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

Comment ça fonctionne

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
la source
1
Il a un bug. Essayez /aa/xavec /ab/y.
jimmy23013
@ user23013: corrigé.
Dennis
2

Bash + coreutils, 116

Voici un script shell pour lancer la balle. Je suis presque sûr qu'il y aura des réponses plus courtes:

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

Production:

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

Notez qu'il n'y a aucun moyen pour le script de dire si la chaîne ftpluginest un fichier ou un répertoire. Vous pouvez explicitement fournir un répertoire en lui ajoutant un /comme dans l'exemple ci-dessus.

Ne gère pas les chemins contenant des espaces ou d'autres personnages amusants. Je ne sais pas si c'est une exigence ou non. Quelques devis supplémentaires seraient nécessaires.

Traumatisme numérique
la source
2

Javascript (E6) 104

Modifier l' alerte ajoutée pour la sortie

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

Non golfé

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

Tester

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
la source
2

Rubis> = 1,9, 89 94 personnages

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

Entrée via des arguments de ligne de commande. Fonctionne pour les chemins de style UNIX et Windows, y compris les chemins avec des noms de dossier répétés:

$ 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
la source
2

J - 63 car

Une fonction prenant l'ancien chemin à gauche et le nouveau chemin à droite.

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

Cette solution se décompose en trois parties post@loop&pre~. Expliqué par l'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

Notez que nous ajoutons un /début à chaque chemin avant la division, afin de gérer les chemins de style Windows en les transformant C:en "dossier". Il en résulte un dossier vide au début des chemins de style Unix, mais qui est toujours supprimé par la boucle.

Voyez-le en action:

   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

Vous pouvez également essayer vous - même à tryj.tk .

algorithmshark
la source
2

Bash, 69 66

Je n'ai pas posté celui-ci parce que je pensais que quelqu'un devait pouvoir le faire beaucoup mieux. Mais apparemment, ce n'est pas si simple.

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

Nfait sedcorrespondre deux lignes ensemble. La première expression supprime le préfixe commun se terminant par /ou \. La deuxième expression remplace les noms de répertoire par ..dans la première ligne. Enfin, il concatène les deux lignes avec le séparateur.

Merci à Hasturkun pour 3 personnages.

jimmy23013
la source
Semble intéressant! Pouvez-vous ajouter une explication?
Digital Trauma
1
@DigitalTrauma Ajouté. Mais en gros, ce ne sont que des expressions régulières.
jimmy23013
Merci! Je vais jouer avec ça la prochaine fois que je serai dans un terminal
Digital Trauma
Vous n'avez pas vraiment besoin d'exécuter seddeux fois, vous pouvez le faire avec un seul script.
Hasturkun
@Hasturkun Mais je n'ai pas trouvé de moyen de le faire fonctionner N. Vous pouvez peut-être modifier cette réponse si vous savez comment.
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
la source
p(char*s,char*d){for(;*s;)*s++-*d?*s-47||printf("../"):d++;puts(d);}68 caractères sans barre oblique inverse
bebe
Merci! Mais la règle 2 stipule que les deux doivent être pris en charge. C'est dans la sortie que je peux choisir l'un ou l'autre (règle 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))

Exemple:

$ python3 path.py
 /usr/share/geany/colorschemes
/usr/share/vim/vim73/ftplugin 
../../vim/vim73/ftplugin
grc
la source
Pourrait-il y avoir un moyen plus court de faire la ligne 1 avec execet les opérations de chaîne?
xnor
@xnor Peut-être, mais je ne le vois pas.
grc
Pourrait map(input,' ')fonctionner pour `(entrée (), entrée ())? (Je ne peux pas le tester moi-même)
xnor
@xnor Ouais ça marche merci!
grc
1

Rubis - 89

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

Usage:

ruby relative.rb working/directory destination/directory
Jwosty
la source
3
Cela échoue pour les arguments comme /foo/bar/foo/baret /foo/qux/foo/bar.
Ventero
Et échoue pour les chemins de style Windows
edc65
@ edc65 Les règles ne disent pas qu'il est nécessaire de prendre en charge les deux formats de chemin, vous pouvez faire l'un ou l'autre.
nderscore
@nderscore Rule 2 Les chemins de style Windows et Unix doivent être pris en charge.
edc65
1
@Jwosty: Eh bien, c'est la beauté, n'est-ce pas? Trouver une solution à la fois courte et correcte. Dans le passé, j'ai eu des cas où j'ai dû réviser complètement la réponse à cause d'un cas de bord négligé. Maintenant, dans ce cas, je blâme également en partie la tâche, car je crois qu'un ensemble solide de cas de test devrait accompagner chaque tâche, mais bien.
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("/")}

Analyse l'un ou l'autre des formats de chemin, mais génère un /séparateur.

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
Mat
la source
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);}

Non golfé:

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
la source
Votre réponse n'est pas correcte. Essayez de créer ces répertoires et de les cdformer les uns aux autres :)
core1024
0

Groovy - 144 caractères

Une solution:

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

exemple de sortie:

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

non golfé:

// 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
la source