Existe uma maneira convencional de combinar seqüências de caracteres de caminho de arquivo?


34

Em um exemplo:

var assets = "images/"

var sounds = assets+"sounds/"

É mais convencional colocar a barra na parte de trás do caminho de um arquivo?

var assets = "/images"

var sounds = assets+"/sounds"

Existe outro método que seja uma boa prática comum?


Java possui as seqüências estáticas File.separator e File.pathSeparator que parecem relevantes. Desta forma, você está seguro em todas as plataformas
Evorlor

1
@Evorlor Você raramente precisa usar File.separator, porém, as APIs Filee Pathaceitam ambos /e `\`.
Kapex

2
Você poderia indicar qual idioma está usando, por favor? Provavelmente vale a pena adicionar a tag correspondente.
Christopher Creutzig

@ChristopherCreutzig Estou usando Java - embora eu estivesse perguntando se havia alguma convenção comumente usada para combinar diretórios de arquivos em strings. Aparentemente, existem algumas regras geralmente aceitas e algum senso comum está envolvido, mas isso varia um pouco de idioma para idioma.
iiridescent

1
Pelo que vale a pena, no mundo unix (e nos URLs), várias barras avançadas no meio de um caminho são tratadas de forma idêntica a uma única, para que nada de ruim aconteça se você errar ao lado de mais barras. Faz parte da especificação Single Unix; Veja esta resposta - unix.stackexchange.com/a/1919/21161
yoniLavi

Respostas:


37

Quase todas as principais linguagens de programação possuem uma biblioteca para lidar com os separadores de diretório para você. Você deve aproveitá-los. Isso simplificará seu código e evitará erros .

Na minha experiência, o motivo usual para combinar seqüências de caracteres como essa é que elas vêm de diferentes fontes. Às vezes, são partes diferentes de um arquivo de configuração. Às vezes, é uma combinação constante com um argumento de função. Em todo e qualquer caso, quando eles vêm de fontes diferentes, é necessário considerar vários casos possíveis diferentes em relação aos separadores nas extremidades a serem combinados:

  • Ambas as extremidades podem ter um separador: "images/"e"/sounds"
  • Apenas um tem um separador: "images"e "/sounds"ou "images/"e"sounds"
  • Nem tem um separador: "images"e"sounds"

O fato de cada parte vir de uma fonte diferente significa que cada fonte pode ter suas próprias idéias sobre quais convenções seguir, se alguém pensar em alguma coisa! O que quer que esteja chamando seu código não deve se preocupar com isso . Seu código deve lidar com todos os casos, porque alguém violará sua convenção . Isso resultará em perda de tempo investigando a causa de um erro e corrigindo. Eu tive várias ocasiões desagradáveis ​​em que um colega de trabalho assumiu como os caminhos devem ser formatados em um arquivo de configuração, o que significa que eu tive que procurar o código e descobrir o que eles estavam esperando (ou consertar o código).

A maioria dos idiomas principais fornece um método para fazer isso para você que já lida com muitos dos casos:

Há uma ressalva com estes. Vários deles parecem supor que um separador de diretório principal no segundo argumento se refira a um caminho raiz e que isso significa que o primeiro argumento deve ser descartado inteiramente. Não sei por que isso é considerado útil; para mim, isso apenas causa problemas. Eu nunca quis combinar duas partes do caminho e terminar com a primeira parte sendo descartada. Leia a documentação cuidadosamente para casos especiais e, se necessário, escreva um invólucro que faça o que você deseja com eles, em vez de seu tratamento especial.

Isso também ajuda se você precisar de suporte para diferentes sistemas operacionais. Essas classes são quase onipresentes na escolha do separador correto. As bibliotecas geralmente também têm uma maneira de normalizar os caminhos para atender às convenções do sistema operacional.

No caso de sua linguagem de programação não ter uma biblioteca prontamente disponível, você deve escrever um método que lide com todos esses casos e use-o livremente e entre projetos.

Isso se enquadra na categoria de "não faça suposições" e "use ferramentas que o ajudem".


2
O Path.Combine do .NET não está quebrado. Só não alimente separadores. certifique-se de ler a documentação, se o segundo argumento for um caminho raiz, ele terá um resultado definido. Você pode não gostar, mas isso não significa que está quebrado.
Erno

4
Leia a documentação para garantir que ela não esteja tentando ser muito inteligente. Certa vez, usei uma biblioteca que poderia ser combinada C:\Documents and Settings\Admincom sucesso my folder:document.txtem um sistema * nix para produzir /home/admin/my folder/document.txt- um truque fofo, mas no mundo real, as heurísticas envolvidas introduziram mais erros do que consertaram.
Mark

1
Além disso, para Java, Paths.get()apenas converte um único Stringem um Pathobjeto. Para ingressar em caminhos, você usaria o Path.resolve()que pode incluir outro Pathou a String. Existem outros métodos na Pathclasse que permitem a junção de caminhos de várias maneiras.
Kat

1
Meu mal, parece que não li os documentos Pathsmuito bem.
Kat

1
No PowerShell, uma alternativa ao método .NET [System.IO.Path]::Combine("abc", "\def")que possui o comportamento descrito, é o cmdlet Join-Path "abc" "\def"fornecido "abc\def".
Jeppe Stig Nielsen

38

Em Java, a resposta seria "nenhuma das opções acima". A melhor prática seria montar nomes de caminho usando a java.io.Fileclasse; por exemplo

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

A Fileclasse também cuida dos separadores de nome de caminho específicos da plataforma.

Há uma questão separada sobre se o nome do caminho deve começar com uma barra ou não. Mas isso tem mais a ver com correção do que com as melhores práticas. Um nome de caminho que começa com uma barra significa algo diferente de um nome de caminho que não !!


Não há suporte explícito para manipulação de nome de caminho na biblioteca Javascript principal (ECMA), mas (pelo menos) o Node.js fornece suporte por meio do módulo Path.


4
Algo semelhante também é o caso das linguagens .Net Framework e quaisquer outras que oferecem classes de sistema de arquivos.
James Snell

3
Obrigado! Essa parecia ser a resposta mais útil, embora as bibliotecas específicas da linguagem devam existir para outras linguagens em geral, como .NET e C ++;
iiridescent

3
Realmente, qualquer código que não use uma biblioteca deve ser rejeitado na revisão de código. Nas raras chances de não existir uma biblioteca, a resposta seria escrever você mesmo, em vez de colar strings não processadas.
Gort the Robot


Python tem os.path.join. O PowerShell possui join-path. Eu acrescentaria algo a esta resposta. Descobri que, se você precisar de caminhos de arquivo em várias partes, isso tornará seu código muito frágil se você fizer suposições sobre qualquer um deles possuindo caminhos de arquivo em locais específicos. O uso dessas classes não apenas ajuda na portabilidade, mas também lida com todos os casos possíveis de borda (barra nas duas extremidades a serem unidas, barra em apenas um lado, nenhuma barra entre elas). Essa flexibilidade é inestimável quando você solta os caminhos de arquivo em um arquivo de configuração.
Jpmc26

21

Observe que no .NET você deve usar o método Path.Combine.

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

A razão para isso é que ele 'conhece' os caracteres corretos a serem usados ​​ao construir os nomes das pastas.

Isso elimina o 'problema' de pré ou pós-fixação.


4
os.path.join faz basicamente a mesma coisa para python, também
StarWeaver

Note-se que Path.Combine não tirá-lo do negócio de se preocupar com o seperator: stackoverflow.com/questions/53102/...
jmoreno

1
@ jmoreno - No meu exemplo, não existem separadores. A pergunta à qual você vinculou possui separadores codificados e se fundamentalmente incorreta, porque o segundo caminho é um caminho absoluto.
Erno

Tenha cuidado com isso, no entanto. Não tenho certeza sobre o .NET, mas os.path.join('src', '../../../your_secret_stuff') é válido em Python; em outras palavras, não use cegamente esses métodos na entrada do usuário.
sapi

@sapi - Obviamente, a entrada do usuário deve sempre ser higienizada, mas essa é a responsabilidade do programador, não da API.
Erno

5

Ao construir caminhos, geralmente uso uma função que adiciona a barra final, se ainda não estiver lá. Em seguida, os caminhos podem ser construídos como:

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

onde fs () adiciona uma barra final, se necessário.


5

Pastas e arquivos diferem apenas em um aspecto: as pastas terminam com uma barra onde os arquivos não. Além disso, os caminhos absolutos começam com um /onde os caminhos relativos não. Se você usar esse processo de concatenação consistente de arquivos e arquivos em conjunto, não haverá 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 dois caminhos absolutos juntos não faz sentido, pois o segundo caminho deve ser relativo ao primeiro caminho. Concatenar dois caminhos relativos juntos não é problema, mas pode levar a um comportamento indefinido se o programa não souber onde o caminho relativo é relativo.


Isso provavelmente respondeu melhor à minha pergunta original, acho que entendo melhor os caminhos dos arquivos, embora, como Stephen C e Erno disseram, as bibliotecas de idiomas sejam a melhor primeira aposta. Isso explica melhor a convenção. Obrigado!
iiridescent

Caminhos ou URLs do sistema de arquivos?
MrWhite

1
Para todos os efeitos, você também pode aplicar isso aos uri's. Um uri absoluto começaria com um protocolo, mas, além disso, seria o mesmo, eu acho.
precisa saber é o seguinte

Não tenho certeza de como sua saída está funcionando. Quando eu fizer isso eu recebo:var a = "/my/path" + "css/" + "test.css"; //Output: "/my/pathcss/test.css"
Damon

1
@ Damon eu fiz uma edição. absolutepathdeveria ter terminado com uma barra, porque é um caminho. De alguma forma, eu ignorei isso quando escrevi isso.
Sumurai8

4

Eu acho que não há mágica ou "prática comum" sobre como implementar caminhos, mas certamente a concatenação de strings não é o caminho a seguir. Você pode desenvolver sua própria API para lidar com casos, mas isso pode exigir algum esforço. Em particular, você deve ter cuidado com as diferentes plataformas. Por exemplo, no Windows \é o separador, enquanto nos sistemas baseados em Unix /é o separador.

Não conheço as bibliotecas Javascript, mas tenho certeza de que deve haver bibliotecas para lidar com esses casos. Em Java, por exemplo, você pode usar a API do Path para lidar com operações de caminho independentes da plataforma.


3
O Windows realmente oferece suporte /como delimitador de nome de arquivo do caminho. Isso precisa de peculiaridades na linha de comando, mas as APIs de E / S de arquivo funcionam bem com a barra.
Ruslan

pt.wikipedia.org/wiki/… "a API do sistema Windows aceita barra e, portanto, todos os exemplos acima do Unix devem funcionar. Mas muitos aplicativos no Windows interpretam uma barra para outros fins ou a tratam como um caractere inválido e, portanto, exigem que você para inserir barra invertida - principalmente o shell cmd.exe (geralmente chamado de "terminal", como normalmente é executado em uma janela de terminal). "
Mooing Duck

0

Minha preferência pessoal é esta:

var assets = "/images"

var sounds = assets+"/sounds"

Eu sempre uso caminhos absolutos ( /images/...), isso me parece menos propenso a erros. Também é mais à prova de idiotas de usar, var sounds = assets+"/sounds"porque, mesmo que assetstivesse uma barra à direita e você acabasse com /images//sounds, ainda assim resolveria /images/sounds. O único aviso é que depende do seu manipulador de solicitações. O Apache parece lidar bem com isso (pelo menos em determinadas versões / configurações, consulte http://www.amazon.com//gp//site-directory//ref=nav_sad ). Por outro lado, você não terá /imagessoundsprovas de idiotas :) Também existe a opção de verificar barras duplas e limpá-las. Não é uma opção com a outra abordagem.


11
Em todos os contextos que eu conheço, um caminho que começa com uma barra ( /) é um caminho absoluto , não um caminho relativo. Ou você quis dizer isso apenas para seções de caminho diferentes da primeira?
Bart van Ingen Schenau

@BartvanIngenSchenau Concordo plenamente com você e os chamo há anos, mas toda vez que leio um artigo escrito por um desenvolvedor de front-end, eles se referem a eles como caminhos relativos. Eu não queria fazer suposições, então acho que escolhi o menor dos dois males ...? Agora que eu sei que tenho algumas pessoas do meu lado eu vou atualizar a minha resposta :)
rpaskett

2
Para desenvolvedores da web, /somewhereé um caminho relativo porque não inclui o host, portanto o navegador procurará com base no host da página atual ... No mundo da web, http://here/somewhereé um URI absoluto e /somewhereelseé relativo a isso. No mundo do sistema de arquivos, /somewhereé absoluto, proveniente da raiz /, e "someelse" é relativo ao diretório de trabalho atual.
Rob

3
@RobY, rpaskett: Indo pelo RFC3986 (o RFC que define URIs), http://here/somewhereé um URI com um caminho absoluto, /somewhereé uma referência relativa com um caminho absoluto e somewhere/elseé uma referência relativa com um caminho relativo. Aparentemente, nesses círculos "caminho relativo" é usado para se referir a uma referência relativa.
Bart van Ingen Schenau

1
@BartvanIngenSchenau: no Windows, um caminho que começa com uma barra é um caminho relativo e é relativo ao CWD. en.wikipedia.org/wiki/…
Mooing Duck

0

No Smalltalk, é fácil definir o método / em String para que funcione assim:

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

Aqui está uma implementação simples do método (você pode torná-lo melhor):

/ 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 : você também pode querer prestar mais atenção aos casos de fronteira, tais como '' / '', 'x/' / '', etc., a fim de determinar o comportamento adequado.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.