¿Hay una forma convencional de combinar cadenas de ruta de archivo?

34

En un ejemplo:

var assets = "images/"

var sounds = assets+"sounds/"

¿Es más convencional poner la barra en la parte posterior de una ruta de archivo?

var assets = "/images"

var sounds = assets+"/sounds"

¿Existe otro método que sea una buena práctica común?

iridiscente
fuente
Java tiene las cadenas estáticas File.separator y File.pathSeparator que suena relevante. De esta manera
estará a
1
@Evorlor Sin File.separatorembargo, rara vez necesita usarlos , Filey las PathAPI aceptan ambos /y `\`.
kapex
2
¿Podría indicar qué idioma está utilizando, por favor? Probablemente valga la pena agregar la etiqueta correspondiente.
Christopher Creutzig
@ChristopherCreutzig Estoy usando Java, aunque estaba preguntando si había alguna convención comúnmente utilizada para combinar directorios de archivos en cadenas. Aparentemente hay algunas reglas generalmente aceptadas y hay algo de sentido común, pero varía un poco de un idioma a otro.
Iridiscente
1
Por lo que vale, en el mundo de Unix (y en las URL), múltiples barras diagonales en el medio de una ruta se tratan de forma idéntica a una sola, por lo que no pasaría nada malo si se equivoca al lado de más barras. Es parte de la especificación Single Unix; ver esta respuesta - unix.stackexchange.com/a/1919/21161
yoniLavi

Respuestas:

37

Casi todos los principales lenguajes de programación tienen una biblioteca para manejar los separadores de directorios por usted. Deberías aprovecharlos. Esto simplificará su código y evitará errores .

En mi experiencia, la razón habitual para combinar cadenas como esta es que provienen de diferentes fuentes. A veces son piezas diferentes de un archivo de configuración. A veces es una combinación constante con un argumento de función. En todos y cada uno de los casos, cuando provienen de diferentes fuentes, debe considerar varios casos posibles diferentes con respecto a los separadores en los extremos que se combinarán:

  • Ambos extremos podrían tener un separador: "images/"y"/sounds"
  • Solo uno tiene un separador: "images"y "/sounds"o "images/"y"sounds"
  • Ninguno tiene un separador: "images"y"sounds"

El hecho de que cada parte provenga de una fuente diferente significa que cada fuente podría tener sus propias ideas sobre qué convenciones seguir, ¡si alguien pensara en ello! Lo que sea que llame a su código no debería tener que preocuparse por esto . Su código debe manejar todos los casos porque alguien violará su convención . Esto dará como resultado una pérdida de tiempo investigando la causa de un error y solucionando el problema. He tenido varias ocasiones desagradables en las que un compañero de trabajo hizo una suposición acerca de cómo se deben formatear las rutas en un archivo de configuración, lo que significa que tuve que buscar el código y averiguar qué estaban esperando (o arreglar el código).

La mayoría de los idiomas principales proporcionan un método para hacerlo que ya maneja muchos de los casos:

Hay una advertencia con estos. Varios de estos parecen suponer que un separador de directorio principal en el segundo argumento se refiere a una ruta raíz y que esto significa que el primer argumento debe descartarse por completo. No sé por qué esto se considera útil; para mí, solo causa problemas. Nunca he querido combinar dos porciones de ruta y terminar con la primera parte que se descarta. Lea la documentación detenidamente para casos especiales y, si es necesario, escriba un contenedor que haga lo que quiera con estos en lugar de su manejo especial.

Esto también ayuda si tiene alguna necesidad de soportar diferentes sistemas operativos. Estas clases casi ubicuamente explican la elección del separador correcto. Por lo general, las bibliotecas también tienen una forma de normalizar rutas para adaptarse a las convenciones del sistema operativo.

En el caso de que su lenguaje de programación no tenga una biblioteca fácilmente disponible, debe escribir un método que maneje todos estos casos y usarlo libremente y en todos los proyectos.

Esto cae en la categoría de "no haga suposiciones" y "use herramientas que lo ayuden".

jpmc26
fuente
2
.NET's Path.Combine no está roto. Simplemente no lo alimente separadores. asegúrese de leer la documentación, si el segundo argumento es una ruta raíz, tiene un resultado definido. Puede que no te guste, pero eso no significa que esté roto.
Erno
44
Asegúrese de leer la documentación para asegurarse de que no está tratando de ser demasiado inteligente. Una vez utilicé una biblioteca que podía combinarse C:\Documents and Settings\Admincon éxito my folder:document.txten un sistema * nix para producir /home/admin/my folder/document.txt, un lindo truco, pero en el mundo real, la heurística involucrada introdujo más errores de los que arreglaron.
Marque el
1
Además, para Java, Paths.get()solo convierte un solo Stringen un Pathobjeto. Para unir rutas, usaría Path.resolve(), que puede incluir otra Patho una String. Hay otros métodos en la Pathclase que permiten unir caminos de varias maneras.
Kat
1
Lo malo, parece que no leí los documentos Pathsmuy bien.
Kat
1
En PowerShell, una alternativa al método .NET [System.IO.Path]::Combine("abc", "\def")que tiene el comportamiento descrito es el cmdlet Join-Path "abc" "\def"que proporciona "abc\def".
Jeppe Stig Nielsen
38

En Java, la respuesta sería "ninguna de las anteriores". La mejor práctica sería ensamblar nombres de ruta utilizando la java.io.Fileclase; p.ej

File assets = new File("images");
File sounds = new File(assets, "sounds");

La Fileclase también se encarga de los separadores de nombre de ruta específicos de la plataforma.

Existe un problema separado de si su nombre de ruta debe comenzar con una barra inclinada o no. Pero eso tiene más que ver con la corrección que con la mejor práctica. ¡Un nombre de ruta que comienza con una barra significa algo diferente a un nombre de ruta que no lo hace!


No hay soporte explícito para el manejo de nombres de ruta en la biblioteca Javascript central (ECMA), pero (al menos) Node.js brinda soporte a través del módulo Path.

Stephen C
fuente
44
Algo similar también es el caso de los lenguajes .Net Framework y cualquier otro que ofrezca clases de sistema de archivos.
James Snell
3
¡Gracias! Esta parecía la respuesta más útil, aunque el lenguaje específico, las bibliotecas deberían existir para otros lenguajes en general, como .NET y C ++;
Iridiscente
3
Realmente, cualquier código que no use una biblioteca debe ser rechazado en la revisión del código. En la rara posibilidad de que no exista una biblioteca, la respuesta sería escribir una usted mismo en lugar de pegar cadenas sin formato.
Gort the Robot
C ++ tiene Boost :: Filesystem , y C # tiene System.IO.Path
Mooing Duck el
Python tiene os.path.join. PowerShell tiene join-path. Yo agregaría algo a esta respuesta. He descubierto que si necesita rutas de archivo en varias partes, hace que su código sea muy frágil si hace suposiciones sobre cualquiera de ellas con rutas de archivo en lugares particulares. El uso de estas clases no solo ayuda con la portabilidad, sino que también maneja todos los casos de borde posibles (barra en ambos extremos para unir, barra en un solo lado, sin barra en absoluto). Esta flexibilidad es invaluable cuando se sueltan rutas de archivos en un archivo de configuración.
jpmc26
21

Tenga en cuenta que en .NET debe usar el método Path.Combine.

var path = System.IO.Path.Combine("assets", "sounds");

La razón de esto es que 'conoce' los caracteres correctos que se utilizarán al construir los nombres de las carpetas.

Esto elimina el "problema" de la fijación previa o posterior.

Erno
fuente
44
os.path.join también hace básicamente lo mismo para Python
StarWeaver
Tenga en cuenta que path.combine no lo saca del negocio de preocuparse por el separador: stackoverflow.com/questions/53102/…
jmoreno
1
@jmoreno: en mi ejemplo NO hay separadores. La pregunta a la que se vinculó tiene separadores codificados y es fundamentalmente incorrecta porque la segunda ruta es absoluta.
Erno
Pero ten cuidado con esto. No estoy seguro acerca de .NET, pero os.path.join('src', '../../../your_secret_stuff') es válido en Python; en otras palabras, no use ciegamente estos métodos en la entrada del usuario.
sapi
@sapi: por supuesto, la entrada del usuario siempre debe desinfectarse, pero eso es responsabilidad del programador, no de la API.
Erno
5

Cuando construyo caminos a menudo uso una función que agrega la barra inclinada final si aún no está allí. Entonces se pueden construir caminos como:

filename := fs( 'assets') + fs( 'images') + fs( 'icons') + 'some.png';

donde fs () agrega una barra inclinada final si es necesario.

Gran maestro B
fuente
5

Las carpetas y los archivos difieren solo en un aspecto: las carpetas terminan con una barra diagonal donde los archivos no. Además, los caminos absolutos comienzan con un /camino relativo donde no lo hacen. Si utiliza esto constantemente concatenar rutas y archivos juntos no debería ser un problema.

var absolutepath = "/my/path/";
var relativepath = "css/";
var filename = "test.css";
var relativepathtofilename = "js/test.js";

var a = absolutepath + relativepath + filename; //Output: /my/path/css/test.css
var b = absolutepath + relativepathtofilename;  //Output: /my/path/js/test.js

Concatenar dos rutas absolutas juntas no tiene sentido, ya que la segunda ruta debe ser relativa a la primera ruta. Concatenar dos rutas relativas juntas no es un problema, pero podría conducir a un comportamiento indefinido si el programa no sabe a dónde se relaciona la ruta relativa.

Sumurai8
fuente
Esto probablemente respondió mejor a mi pregunta original, creo que entiendo mejor las rutas de archivo, aunque, como dijeron Stephen C y Erno, las bibliotecas de idiomas son la mejor opción. Sin embargo, esto explica mejor la convención. ¡Gracias!
Iridiscente
¿Rutas del sistema de archivos o URL?
MrWhite
1
Para todos los efectos, puede aplicar esto también en uri. Una uri absoluta comenzaría con un protocolo, pero además de eso, creo que sería lo mismo.
Sumurai8
No estoy seguro de cómo funciona su salida. Cuando lo hago me sale:var a = "/my/path" + "css/" + "test.css"; //Output: "/my/pathcss/test.css"
Damon
1
@ Damon hice una edición. absolutepathdebería haber terminado con una barra oblicua, porque es un camino. De alguna manera pasé por alto eso cuando escribí esto.
Sumurai8
4

Creo que no hay magia o "práctica común" sobre cómo implementar rutas, pero ciertamente la concatenación de cadenas no es el camino a seguir. Puede desarrollar su propia API para tratar casos, pero puede requerir cierto esfuerzo. En particular, debe tener cuidado con las diferentes plataformas. Por ejemplo, en Windows \es el separador, mientras que en los sistemas basados ​​en Unix /es el separador.

No estoy familiarizado con las bibliotecas de Javascript, pero estoy seguro de que debería haber bibliotecas para manejar estos casos. En Java, por ejemplo, puede usar la API de ruta para tratar las operaciones de ruta independientes de la plataforma.

Wickoo
fuente
3
Windows realmente admite /como delimitador de nombre de ruta. Esto necesita peculiaridades en la línea de comandos, pero las API de E / S de archivos funcionan bien con la barra diagonal.
Ruslan
en.wikipedia.org/wiki/… "la API del sistema de Windows acepta barra oblicua, y por lo tanto todos los ejemplos de Unix anteriores deberían funcionar. Pero muchas aplicaciones en Windows interpretan una barra oblicua para otros fines o la tratan como un carácter no válido, y por lo tanto requieren que usted para ingresar la barra diagonal inversa, especialmente el shell cmd.exe (a menudo llamado "terminal", ya que normalmente se ejecuta en una ventana de terminal) ".
Mooing Duck
0

Mi preferencia personal es esta:

var assets = "/images"

var sounds = assets+"/sounds"

Siempre uso rutas absolutas ( /images/...), me parece menos propenso a errores. También es una prueba más tonta de usar var sounds = assets+"/sounds"porque incluso si assetstuviera una barra inclinada final y terminara /images//sounds, aún se resolvería /images/sounds. El descargo de responsabilidad es que depende de su controlador de solicitud. Apache parece manejarlo bien (al menos ciertas versiones / configuraciones, consulte http://www.amazon.com//gp//site-directory//ref=nav_sad ). De la otra manera que terminaría /imagessounds, no es tan infalible :) También existe la opción de verificar si hay barras dobles y limpiarlas. No es una opción con el otro enfoque.

rpaskett
fuente
11
En todos los contextos que conozco, una ruta que comienza con una barra inclinada ( /) es una ruta absoluta , no relativa. ¿O lo quiso decir solo para secciones de ruta distintas a la primera?
Bart van Ingen Schenau
@BartvanIngenSchenau Estoy totalmente de acuerdo con usted y los he estado llamando así durante años, pero cada vez que leo un artículo escrito por un desarrollador front-end se refieren a ellos como caminos relativos. No quería hacer suposiciones, así que supongo que elegí el menor de los dos males ... Ahora que sé que tengo algunas personas de mi lado, actualizaré mi respuesta :)
rpaskett
2
Para los desarrolladores web, /somewherees una ruta relativa porque no incluye el host, por lo que el navegador lo buscará en función del host de la página actual ... En el mundo web, http://here/somewherees un URI absoluto, y /somewhereelsees relativo a eso. En el mundo del sistema de archivos, /somewherees absoluto, proviene de la raíz /, y "en otro lugar" es relativo al directorio de trabajo actual.
Rob
3
@RobY, rpaskett: según RFC3986 (el RFC que define los URI), http://here/somewherees un URI con una ruta absoluta, /somewherees una referencia relativa con una ruta absoluta y somewhere/elsees una referencia relativa con una ruta relativa. Aparentemente, en esos círculos "ruta relativa" se usa para referirse a una referencia relativa.
Bart van Ingen Schenau
1
@BartvanIngenSchenau: en Windows, una ruta que comienza con una barra diagonal es una ruta relativa y es relativa a CWD. en.wikipedia.org/wiki/…
Mooing Duck
0

En Smalltalk es sencillo definir el método / en String para que funcione así:

'assets' / 'sounds' => 'assets/sounds'.
'assets/' / 'sounds' => 'assets/sounds'.
'assets' / '/sounds' => 'assets/sounds'.
'assets/' / '/sounds' => 'assets/sounds'.

Aquí hay una implementación simple del método (puede mejorarlo):

/ aString
    | slash first second |
    slash := Directory separator.
    first := self.
    (first endsWith: slash) ifTrue: [first := first allButLast].
    second := aString.
    (second beginsWith: slash) ifTrue: [second := second allButFirst].
    ^first , slash , second

Nota : También puede ser que desee prestar más atención a los casos fronterizos, tales como '' / '', 'x/' / '', etc., con el fin de determinar el comportamiento apropiado.

Leandro Caniglia
fuente