Imprimir la ruta relativa

15

Descripción

Dada una ruta de origen y una ruta de destino, genera la ruta relativa al destino con respecto a la fuente.

Reglas

  1. La entrada puede provenir de stdin o como argumentos para el programa / función.

  2. Tanto las rutas de estilo Windows como Unix deben ser compatibles.

  3. La ruta de salida puede usar /y / o \para el separador de ruta (su elección y combinación de ambas está bien).

  4. Puede suponer que es posible una ruta relativa.

  5. El uso de programas externos, funciones incorporadas o de biblioteca hechas para calcular rutas relativas está prohibido (por ejemplo, Python os.path.relpath)

  6. Esto es

    Editar: nueva regla de los comentarios.

  7. La ruta relativa debe ser la ruta relativa más corta posible.

  8. Suponga que la ruta de destino es diferente de la ruta de origen.

Ejemplo 1

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

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

Ejemplo 2

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

# Out
..\WindowsPowerShell\v1.0
Rynant
fuente
Con respecto a la regla # 3, ¿está bien una mezcla? Por ej ../../vim\vim73\ftplugin.
Duncan Jones
1
¿Tenemos que devolver el camino relativo más corto o está bien ceder algún camino?
Howard
@ Duncan Sí, una mezcla está bien.
Rynant
1
@Howard, debe ser el camino relativo más corto.
Rynant
¿No debería ser el primer ejemplo ../vim/vim73/ftplugin?
Martijn

Respuestas:

2

CJam, 46 bytes

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

Pruébalo en línea.

Ejemplos

$ 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

Cómo funciona

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
fuente
1
Tiene un error Tratar /aa/xcon /ab/y.
jimmy23013
@ user23013: corregido.
Dennis
2

Bash + coreutils, 116

Aquí hay un script de shell para que la pelota ruede. Estoy bastante seguro de que habrá respuestas más cortas:

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

Salida:

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

Tenga en cuenta que no hay forma de que el script indique si la cadena ftplugines un archivo o un directorio. Puede proporcionar explícitamente un directorio agregándolo con un /como en el ejemplo anterior.

No manejará rutas que contengan espacios en blanco u otros personajes divertidos. No estoy seguro si eso es un requisito o no. Solo se necesitarían algunas citas adicionales.

Trauma digital
fuente
2

Javascript (E6) 104

Editar alerta agregada para salida

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

Sin 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

Prueba

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

Rubí> = 1.9, 89 94 caracteres

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

Entrada a través de argumentos de línea de comando. Funciona para rutas de estilo UNIX y Windows, incluidas rutas con nombres de carpeta repetidos:

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

J - 63 char

Una función que toma la ruta anterior a la izquierda y la nueva ruta a la derecha.

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

Esta solución viene en tres partes, parecidas post@loop&pre~. Explicado por explosión:

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

Tenga en cuenta que agregamos una guía /a cada ruta antes de dividirla, de modo que manejemos las rutas al estilo de Windows al convertirlas C:en una "carpeta". Esto da como resultado una carpeta vacía al comienzo de las rutas de estilo Unix, pero que siempre se elimina con el bucle.

Véalo en acción:

   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

También puede hacerlo por uno mismo en tryj.tk .

Algoritmo de tiburón
fuente
2

Bash, 69 66

No publiqué este porque pensé que alguien debía poder hacerlo mucho mejor. Pero aparentemente no es tan fácil.

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

Nhace sedcoincidir dos líneas juntas. La primera expresión elimina el prefijo común que termina con /o \. La segunda expresión reemplaza los nombres de directorio con ..en la primera línea. Finalmente concatena las dos líneas con el separador.

Gracias a Hasturkun por 3 personajes.

jimmy23013
fuente
¡Parece interesante! ¿Puedes agregar una explicación?
Trauma digital
1
@DigitalTrauma agregado. Pero básicamente son solo expresiones regulares.
jimmy23013
¡Gracias! Voy a jugar con esto la próxima vez que esté en una terminal
Digital Trauma
Realmente no necesita ejecutar seddos veces, puede hacerlo con un solo script.
Hasturkun
@Hasturkun Pero no encontré una manera de hacerlo funcionar N. Tal vez puedas editar esta respuesta si sabes cómo.
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
fuente
p(char*s,char*d){for(;*s;)*s++-*d?*s-47||printf("../"):d++;puts(d);}68 caracteres sin barra invertida
bebe
¡Gracias! Pero la regla 2 dice que ambos deben ser compatibles. Es en la salida donde puedo elegir uno u otro (regla 3).
kwokkie
1

Pitón 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))

Ejemplo:

$ python3 path.py
 /usr/share/geany/colorschemes
/usr/share/vim/vim73/ftplugin 
../../vim/vim73/ftplugin
grc
fuente
¿Podría haber una forma más corta de hacer la línea 1 con execy las operaciones de cadena?
xnor
@xnor Quizás, pero no puedo verlo.
grc
¿Podría map(input,' ')funcionar para `(input (), input ())? (No puedo probarlo yo mismo)
xnor
@xnor Sí, eso funciona, gracias!
grc
1

Rubí - 89

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

Uso:

ruby relative.rb working/directory destination/directory
Jwosty
fuente
3
Esto falla para argumentos como /foo/bar/foo/bary /foo/qux/foo/bar.
Ventero
Y falla para las rutas de estilo de Windows
edc65
@ edc65 Las reglas no dicen que sea necesario admitir ambos formatos de ruta, puede hacer cualquiera de los dos.
nderscore
@nderscore Regla 2 Se deben admitir las rutas de estilo Windows y Unix.
edc65
1
@Jwosty: Bueno, esa es la belleza, ¿no? Proponiendo una solución que sea breve y correcta. En el pasado tuve casos en los que tuve que revisar la respuesta por completo debido a un caso marginal pasado por alto. Ahora, en este caso, también culpo en parte a la tarea, porque creo que un conjunto sólido de casos de prueba debería acompañar a cada tarea, pero 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("/")}

Analiza cualquier formato de ruta pero sale con /separador.

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
Mate
fuente
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);}

Sin 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
fuente
Tu respuesta no es correcta. Intente hacer estos directorios y cdforme uno a otro :)
core1024
0

Groovy - 144 caracteres

Una solución:

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

salida de ejemplo:

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

sin 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
fuente