Para Java 7 e posterior, consulte a resposta de @ VitaliiFedorenko.
Andy Thomas
1
tl; dr resposta: Paths.get (startPath) .relativize (Paths.get (endPath)). toString () (que, a propósito, parece estar funcionando muito bem com, por exemplo, "../" para mim no Java 8 , então ...)
Andrew
Respostas:
298
É um pouco indireto, mas por que não usar URI? Ele tem um método de relativização que faz todas as verificações necessárias para você.
String path ="/var/data/stuff/xyz.dat";String base ="/var/data";String relative =newFile(base).toURI().relativize(newFile(path).toURI()).getPath();// relative == "stuff/xyz.dat"
Observe que para o caminho do arquivo existe java.nio.file.Path#relativizedesde o Java 1.7, como apontado por @Jirka Meluzin na outra resposta .
Veja a resposta de Peter Mueller. relativize () parece bastante quebrado para todos, exceto os casos mais simples.
21411 Dave Ray
11
Sim, só funciona se o caminho base for um pai do primeiro caminho. Se você precisar de algum retorno hierárquico como "../../caminho relativo", ele não funcionará. Eu encontrei uma solução: mrpmorris.blogspot.com/2007/05/…
Aurelien Ribon
4
Como o @VitaliiFedorenko escreveu: use java.nio.file.Path#relativize(Path), ele funciona apenas com os pontos duplos dos pais e tudo.
Campa 12/02
Considere usar em toPath()vez de toURI(). É perfeitamente capaz de criar coisas como "..\..". Mas lembre-se da java.lang.IllegalArgumentException: 'other' has different rootexceção ao solicitar o caminho relativo de "C:\temp"para "D:\temp".
Igor
Isso não funciona conforme o esperado, ele retorna data / stuff / xyz.dat no meu caso de teste.
unbekant
238
Desde o Java 7, você pode usar o método relativize :
Bom, curto, sem lib extra +1. A solução de Adam Crume (hit 1) não passa nos meus testes e a próxima resposta (hit2) "The Only 'Working' Solution" adiciona um novo jar E é mais código do que minha implementação, acho isso aqui depois ... melhor do que nunca. )
Verificou se isso lida com a adição sempre ..que necessário (faz).
Owen
Infelizmente, o Android não inclui java.nio.file:(
Nathan Osman
1
Descobri que você obtém resultados estranhos se o "pathBase" não for "normalizado" antes de "relativizar". Embora bem neste exemplo, eu faria pathBase.normalize().relativize(pathAbsolute);como regra geral.
pstanton
77
No momento em que escrevi (junho de 2010), essa era a única solução que passou nos meus casos de teste. Não posso garantir que esta solução esteja livre de erros, mas ela passa nos casos de teste incluídos. O método e os testes que escrevi dependem da FilenameUtilsclasse do IO do Apache commons .
A solução foi testada com Java 1.4. Se você estiver usando o Java 1.5 (ou superior), considere substituir StringBufferpor StringBuilder(se ainda estiver usando o Java 1.4, considere uma mudança de empregador).
import java.io.File;import java.util.regex.Pattern;import org.apache.commons.io.FilenameUtils;publicclassResourceUtils{/**
* Get the relative path from one file to another, specifying the directory separator.
* If one of the provided resources does not exist, it is assumed to be a file unless it ends with '/' or
* '\'.
*
* @param targetPath targetPath is calculated to this file
* @param basePath basePath is calculated from this file
* @param pathSeparator directory separator. The platform default is not assumed so that we can test Unix behaviour when running on Windows (for example)
* @return
*/publicstaticString getRelativePath(String targetPath,String basePath,String pathSeparator){// Normalize the pathsString normalizedTargetPath =FilenameUtils.normalizeNoEndSeparator(targetPath);String normalizedBasePath =FilenameUtils.normalizeNoEndSeparator(basePath);// Undo the changes to the separators made by normalizationif(pathSeparator.equals("/")){
normalizedTargetPath =FilenameUtils.separatorsToUnix(normalizedTargetPath);
normalizedBasePath =FilenameUtils.separatorsToUnix(normalizedBasePath);}elseif(pathSeparator.equals("\\")){
normalizedTargetPath =FilenameUtils.separatorsToWindows(normalizedTargetPath);
normalizedBasePath =FilenameUtils.separatorsToWindows(normalizedBasePath);}else{thrownewIllegalArgumentException("Unrecognised dir separator '"+ pathSeparator +"'");}String[] base = normalizedBasePath.split(Pattern.quote(pathSeparator));String[] target = normalizedTargetPath.split(Pattern.quote(pathSeparator));// First get all the common elements. Store them as a string,// and also count how many of them there are.StringBuffer common =newStringBuffer();int commonIndex =0;while(commonIndex < target.length && commonIndex < base.length
&& target[commonIndex].equals(base[commonIndex])){
common.append(target[commonIndex]+ pathSeparator);
commonIndex++;}if(commonIndex ==0){// No single common path element. This most// likely indicates differing drive letters, like C: and D:.// These paths cannot be relativized.thrownewPathResolutionException("No common path element found for '"+ normalizedTargetPath +"' and '"+ normalizedBasePath
+"'");}// The number of directories we have to backtrack depends on whether the base is a file or a dir// For example, the relative path from//// /foo/bar/baz/gg/ff to /foo/bar/baz// // ".." if ff is a file// "../.." if ff is a directory//// The following is a heuristic to figure out if the base refers to a file or dir. It's not perfect, because// the resource referred to by this path may not actually exist, but it's the best I can doboolean baseIsFile =true;File baseResource =newFile(normalizedBasePath);if(baseResource.exists()){
baseIsFile = baseResource.isFile();}elseif(basePath.endsWith(pathSeparator)){
baseIsFile =false;}StringBuffer relative =newStringBuffer();if(base.length != commonIndex){int numDirsUp = baseIsFile ? base.length - commonIndex -1: base.length - commonIndex;for(int i =0; i < numDirsUp; i++){
relative.append(".."+ pathSeparator);}}
relative.append(normalizedTargetPath.substring(common.length()));return relative.toString();}staticclassPathResolutionExceptionextendsRuntimeException{PathResolutionException(String msg){super(msg);}}}
Os casos de teste que são aprovados são
publicvoid testGetRelativePathsUnix(){
assertEquals("stuff/xyz.dat",ResourceUtils.getRelativePath("/var/data/stuff/xyz.dat","/var/data/","/"));
assertEquals("../../b/c",ResourceUtils.getRelativePath("/a/b/c","/a/x/y/","/"));
assertEquals("../../b/c",ResourceUtils.getRelativePath("/m/n/o/a/b/c","/m/n/o/a/x/y/","/"));}publicvoid testGetRelativePathFileToFile(){String target ="C:\\Windows\\Boot\\Fonts\\chs_boot.ttf";String base ="C:\\Windows\\Speech\\Common\\sapisvr.exe";String relPath =ResourceUtils.getRelativePath(target, base,"\\");
assertEquals("..\\..\\Boot\\Fonts\\chs_boot.ttf", relPath);}publicvoid testGetRelativePathDirectoryToFile(){String target ="C:\\Windows\\Boot\\Fonts\\chs_boot.ttf";String base ="C:\\Windows\\Speech\\Common\\";String relPath =ResourceUtils.getRelativePath(target, base,"\\");
assertEquals("..\\..\\Boot\\Fonts\\chs_boot.ttf", relPath);}publicvoid testGetRelativePathFileToDirectory(){String target ="C:\\Windows\\Boot\\Fonts";String base ="C:\\Windows\\Speech\\Common\\foo.txt";String relPath =ResourceUtils.getRelativePath(target, base,"\\");
assertEquals("..\\..\\Boot\\Fonts", relPath);}publicvoid testGetRelativePathDirectoryToDirectory(){String target ="C:\\Windows\\Boot\\";String base ="C:\\Windows\\Speech\\Common\\";String expected ="..\\..\\Boot";String relPath =ResourceUtils.getRelativePath(target, base,"\\");
assertEquals(expected, relPath);}publicvoid testGetRelativePathDifferentDriveLetters(){String target ="D:\\sources\\recovery\\RecEnv.exe";String base ="C:\\Java\\workspace\\AcceptanceTests\\Standard test data\\geo\\";try{ResourceUtils.getRelativePath(target, base,"\\");
fail();}catch(PathResolutionException ex){// expected exception}}
Agradável! Uma coisa, no entanto, é interrompida se a base e o destino forem os mesmos - a string comum é feita para terminar em um separador, que o caminho do destino normalizado não possui, portanto a chamada de substring solicita um número excessivo de dígitos. Acho que eu o corrigi adicionando o seguinte antes das duas últimas linhas da função: if (common.length ()> = normalizedTargetPath.length ()) {return "."; }
Erhannis
4
Dizer que essa é a única solução funcional é enganoso. Outras respostas funcionam melhor (essa resposta falha quando a base e o alvo são os mesmos), são mais simples e não dependem do commons-io.
Paths.get (startPath) .relativize (Paths.get (endPath)) toString () parece estar funcionando muito bem com por exemplo, "../" para mim em Java 8..
Andrew
@skaffman você tem certeza? Esta resposta faz referência ao bug JDK-6226081, mas URIUtils.resolve()menciona JDK-4708535. E do código fonte, não vejo nada relacionado ao retorno (ou seja, ..segmentos). Você confundiu os dois erros?
Garret Wilson
JDK-6920138 está marcado como uma duplicata de JDK-4708535.
Christian K.
18
No Java 7 e posterior, você pode simplesmente usar (e, ao contrário URI, é livre de erros):
String s3 = "." + s1.substring (s2.length ()); é um pouco mais legível IMO
Dónal 15/10/08
10
A recursão produz uma solução menor. Isso gera uma exceção se o resultado for impossível (por exemplo, disco diferente do Windows) ou impraticável (raiz é apenas o diretório comum).
/**
* Computes the path for a file relative to a given base, or fails if the only shared
* directory is the root and the absolute form is better.
*
* @param base File that is the base for the result
* @param name File to be "relativized"
* @return the relative name
* @throws IOException if files have no common sub-directories, i.e. at best share the
* root prefix "/" or "C:\"
*/publicstaticString getRelativePath(File base,File name)throwsIOException{File parent = base.getParentFile();if(parent ==null){thrownewIOException("No common directory");}String bpath = base.getCanonicalPath();String fpath = name.getCanonicalPath();if(fpath.startsWith(bpath)){return fpath.substring(bpath.length()+1);}else{return(".."+File.separator + getRelativePath(parent, name));}}
O getCanonicalPath pode ser pesado, portanto, essa solução não é recomendada quando você precisa processar cem mil registros. Por exemplo, tenho alguns arquivos de listagem com até milhões de registros e agora quero movê-los para usar o caminho relativo para portabilidade.
user2305886
8
Aqui está uma solução gratuita para outras bibliotecas:
Minha versão é vagamente baseada nas versões de Matt e Steve :
/**
* Returns the path of one File relative to another.
*
* @param target the target directory
* @param base the base directory
* @return target's path relative to the base directory
* @throws IOException if an error occurs while resolving the files' canonical names
*/publicstaticFile getRelativeFile(File target,File base)throwsIOException{String[] baseComponents = base.getCanonicalPath().split(Pattern.quote(File.separator));String[] targetComponents = target.getCanonicalPath().split(Pattern.quote(File.separator));// skip common componentsint index =0;for(; index < targetComponents.length && index < baseComponents.length;++index){if(!targetComponents[index].equals(baseComponents[index]))break;}StringBuilder result =newStringBuilder();if(index != baseComponents.length){// backtrack to base directoryfor(int i = index; i < baseComponents.length;++i)
result.append(".."+File.separator);}for(; index < targetComponents.length;++index)
result.append(targetComponents[index]+File.separator);if(!target.getPath().endsWith("/")&&!target.getPath().endsWith("\\")){// remove final path separator
result.delete(result.length()-File.separator.length(), result.length());}returnnewFile(result.toString());}
+1 funciona para mim. Apenas pequenas correção: em vez de "/".length()você deve usar separator.length
leonbloy
5
A solução de Matt B faz com que o número de diretórios retorne errado - deve ser o comprimento do caminho base menos o número de elementos comuns do caminho, menos um (para o último elemento do caminho, que é um nome de arquivo ou um resultado ""gerado por split) . Acontece que ele trabalha com /a/b/c/e /a/x/y/, mas substitui os argumentos por /m/n/o/a/b/c/e/m/n/o/a/x/y/ e você verá o problema.
Além disso, ele precisa de um else breakdentro do primeiro loop for, ou manipulará incorretamente os caminhos que possuem nomes de diretório correspondentes, como /a/b/c/d/e /x/y/c/z- oc está no mesmo slot nas duas matrizes, mas não é uma correspondência real.
Todas essas soluções não têm a capacidade de lidar com caminhos que não podem ser relativizados entre si porque possuem raízes incompatíveis, como C:\foo\bareD:\baz\quux . Provavelmente apenas um problema no Windows, mas vale a pena notar.
Passei muito mais tempo nisso do que pretendia, mas tudo bem. Na verdade, eu precisava disso para trabalhar, por isso agradeço a todos que participaram, e tenho certeza de que também haverá correções nesta versão!
publicstaticString getRelativePath(String targetPath,String basePath,String pathSeparator){// We need the -1 argument to split to make sure we get a trailing // "" token if the base ends in the path separator and is therefore// a directory. We require directory paths to end in the path// separator -- otherwise they are indistinguishable from files.String[] base = basePath.split(Pattern.quote(pathSeparator),-1);String[] target = targetPath.split(Pattern.quote(pathSeparator),0);// First get all the common elements. Store them as a string,// and also count how many of them there are. String common ="";int commonIndex =0;for(int i =0; i < target.length && i < base.length; i++){if(target[i].equals(base[i])){
common += target[i]+ pathSeparator;
commonIndex++;}elsebreak;}if(commonIndex ==0){// Whoops -- not even a single common path element. This most// likely indicates differing drive letters, like C: and D:. // These paths cannot be relativized. Return the target path.return targetPath;// This should never happen when all absolute paths// begin with / as in *nix. }String relative ="";if(base.length == commonIndex){// Comment this out if you prefer that a relative path not start with .///relative = "." + pathSeparator;}else{int numDirsUp = base.length - commonIndex -1;// The number of directories we have to backtrack is the length of // the base path MINUS the number of common path elements, minus// one because the last element in the path isn't a directory.for(int i =1; i <=(numDirsUp); i++){
relative +=".."+ pathSeparator;}}
relative += targetPath.substring(common.length());return relative;}
E aqui estão os testes para cobrir vários casos:
publicvoid testGetRelativePathsUnixy(){
assertEquals("stuff/xyz.dat",FileUtils.getRelativePath("/var/data/stuff/xyz.dat","/var/data/","/"));
assertEquals("../../b/c",FileUtils.getRelativePath("/a/b/c","/a/x/y/","/"));
assertEquals("../../b/c",FileUtils.getRelativePath("/m/n/o/a/b/c","/m/n/o/a/x/y/","/"));}publicvoid testGetRelativePathFileToFile(){String target ="C:\\Windows\\Boot\\Fonts\\chs_boot.ttf";String base ="C:\\Windows\\Speech\\Common\\sapisvr.exe";String relPath =FileUtils.getRelativePath(target, base,"\\");
assertEquals("..\\..\\..\\Boot\\Fonts\\chs_boot.ttf", relPath);}publicvoid testGetRelativePathDirectoryToFile(){String target ="C:\\Windows\\Boot\\Fonts\\chs_boot.ttf";String base ="C:\\Windows\\Speech\\Common";String relPath =FileUtils.getRelativePath(target, base,"\\");
assertEquals("..\\..\\Boot\\Fonts\\chs_boot.ttf", relPath);}publicvoid testGetRelativePathDifferentDriveLetters(){String target ="D:\\sources\\recovery\\RecEnv.exe";String base ="C:\\Java\\workspace\\AcceptanceTests\\Standard test data\\geo\\";// Should just return the target path because of the incompatible roots.String relPath =FileUtils.getRelativePath(target, base,"\\");
assertEquals(target, relPath);}
Na verdade, minha outra resposta não funcionaria se o caminho de destino não fosse filho do caminho base.
Isso deve funcionar.
publicclassRelativePathFinder{publicstaticString getRelativePath(String targetPath,String basePath,String pathSeparator){// find common pathString[] target = targetPath.split(pathSeparator);String[] base = basePath.split(pathSeparator);String common ="";int commonIndex =0;for(int i =0; i < target.length && i < base.length; i++){if(target[i].equals(base[i])){
common += target[i]+ pathSeparator;
commonIndex++;}}String relative ="";// is the target a child directory of the base directory?// i.e., target = /a/b/c/d, base = /a/b/if(commonIndex == base.length){
relative ="."+ pathSeparator + targetPath.substring(common.length());}else{// determine how many directories we have to backtrackfor(int i =1; i <= commonIndex; i++){
relative +=".."+ pathSeparator;}
relative += targetPath.substring(common.length());}return relative;}publicstaticString getRelativePath(String targetPath,String basePath){return getRelativePath(targetPath, basePath,File.pathSeparator);}}
Em vez de File.pathSeparator deve ser File.separator. pathSeparator deve usar apenas para divisão (regex), pois para "////" regex (win path regex), o caminho do resultado estará incorreto.
Alex Ivasyuv 29/03/10
3
Legal!! Eu preciso de um pouco de código como este, mas para comparar os caminhos de diretório em máquinas Linux. Descobri que isso não estava funcionando em situações em que um diretório pai era o alvo.
Aqui está uma versão amigável do diretório do método:
publicstaticString getRelativePath(String targetPath,String basePath,String pathSeparator){boolean isDir =false;{File f =newFile(targetPath);
isDir = f.isDirectory();}// We need the -1 argument to split to make sure we get a trailing // "" token if the base ends in the path separator and is therefore// a directory. We require directory paths to end in the path// separator -- otherwise they are indistinguishable from files.String[] base = basePath.split(Pattern.quote(pathSeparator),-1);String[] target = targetPath.split(Pattern.quote(pathSeparator),0);// First get all the common elements. Store them as a string,// and also count how many of them there are. String common ="";int commonIndex =0;for(int i =0; i < target.length && i < base.length; i++){if(target[i].equals(base[i])){
common += target[i]+ pathSeparator;
commonIndex++;}elsebreak;}if(commonIndex ==0){// Whoops -- not even a single common path element. This most// likely indicates differing drive letters, like C: and D:. // These paths cannot be relativized. Return the target path.return targetPath;// This should never happen when all absolute paths// begin with / as in *nix. }String relative ="";if(base.length == commonIndex){// Comment this out if you prefer that a relative path not start with ./
relative ="."+ pathSeparator;}else{int numDirsUp = base.length - commonIndex -(isDir?0:1);/* only subtract 1 if it is a file. */// The number of directories we have to backtrack is the length of // the base path MINUS the number of common path elements, minus// one because the last element in the path isn't a directory.for(int i =1; i <=(numDirsUp); i++){
relative +=".."+ pathSeparator;}}//if we are comparing directories then we if(targetPath.length()> common.length()){//it's OK, it isn't a directory
relative += targetPath.substring(common.length());}return relative;}
Suponho que você tenha fromPath (um caminho absoluto para uma pasta) e toPath (um caminho absoluto para uma pasta / arquivo), e você está procurando um caminho que represente o arquivo / pasta no toPath como um caminho relativo from fromPath (seu diretório de trabalho atual é fromPath ), algo assim deve funcionar:
publicstaticString getRelativePath(String fromPath,String toPath){// This weirdness is because a separator of '/' messes with String.split()String regexCharacter =File.separator;if(File.separatorChar =='\\'){
regexCharacter ="\\\\";}String[] fromSplit = fromPath.split(regexCharacter);String[] toSplit = toPath.split(regexCharacter);// Find the common pathint common =0;while(fromSplit[common].equals(toSplit[common])){
common++;}StringBuffer result =newStringBuffer(".");// Work your way up the FROM path to common groundfor(int i = common; i < fromSplit.length; i++){
result.append(File.separatorChar).append("..");}// Work your way down the TO pathfor(int i = common; i < toSplit.length; i++){
result.append(File.separatorChar).append(toSplit[i]);}return result.toString();}
Muitas respostas já estão aqui, mas descobri que elas não lidavam com todos os casos, como a base e o destino, sendo os mesmos. Essa função pega um diretório base e um caminho de destino e retorna o caminho relativo. Se não existir um caminho relativo, o caminho de destino será retornado. File.separator é desnecessário.
publicstaticString getRelativePath (String baseDir,String targetPath){String[] base = baseDir.replace('\\','/').split("\\/");
targetPath = targetPath.replace('\\','/');String[] target = targetPath.split("\\/");// Count common elements and their length.int commonCount =0, commonLength =0, maxCount =Math.min(target.length, base.length);while(commonCount < maxCount){String targetElement = target[commonCount];if(!targetElement.equals(base[commonCount]))break;
commonCount++;
commonLength += targetElement.length()+1;// Directory name length plus slash.}if(commonCount ==0)return targetPath;// No common path element.int targetLength = targetPath.length();int dirsUp = base.length - commonCount;StringBuffer relative =newStringBuffer(dirsUp *3+ targetLength - commonLength +1);for(int i =0; i < dirsUp; i++)
relative.append("../");if(commonLength < targetLength) relative.append(targetPath.substring(commonLength));return relative.toString();}
Aqui, um método que resolve um caminho relativo a partir de um caminho base, independentemente de estarem na mesma raiz ou em uma raiz diferente:
publicstaticStringGetRelativePath(String path,String base){finalString SEP ="/";// if base is not a directory -> return emptyif(!base.endsWith(SEP)){return"";}// check if path is a file -> remove last "/" at the end of the methodboolean isfile =!path.endsWith(SEP);// get URIs and split them by using the separatorString a ="";String b ="";try{
a =newFile(base).getCanonicalFile().toURI().getPath();
b =newFile(path).getCanonicalFile().toURI().getPath();}catch(IOException e){
e.printStackTrace();}String[] basePaths = a.split(SEP);String[] otherPaths = b.split(SEP);// check common partint n =0;for(; n < basePaths.length && n < otherPaths.length; n ++){if( basePaths[n].equals(otherPaths[n])==false)break;}// compose the new pathStringBuffer tmp =newStringBuffer("");for(int m = n; m < basePaths.length; m ++)
tmp.append(".."+SEP);for(int m = n; m < otherPaths.length; m ++){
tmp.append(otherPaths[m]);
tmp.append(SEP);}// get path stringString result = tmp.toString();// remove last "/" if path is a fileif(isfile && result.endsWith(SEP)){
result = result.substring(0,result.length()-1);}return result;}
Divida as seqüências pelo separador de caminho ("/")
Encontre o melhor caminho comum iterando pelo resultado da cadeia de caracteres dividida (para que você acabe com "/ var / data" ou "/ a" em seus dois exemplos)
Respostas:
É um pouco indireto, mas por que não usar URI? Ele tem um método de relativização que faz todas as verificações necessárias para você.
Observe que para o caminho do arquivo existe
java.nio.file.Path#relativize
desde o Java 1.7, como apontado por @Jirka Meluzin na outra resposta .fonte
java.nio.file.Path#relativize(Path)
, ele funciona apenas com os pontos duplos dos pais e tudo.toPath()
vez detoURI()
. É perfeitamente capaz de criar coisas como"..\.."
. Mas lembre-se dajava.lang.IllegalArgumentException: 'other' has different root
exceção ao solicitar o caminho relativo de"C:\temp"
para"D:\temp"
.Desde o Java 7, você pode usar o método relativize :
Resultado:
fonte
..
que necessário (faz).java.nio.file
:(pathBase.normalize().relativize(pathAbsolute);
como regra geral.No momento em que escrevi (junho de 2010), essa era a única solução que passou nos meus casos de teste. Não posso garantir que esta solução esteja livre de erros, mas ela passa nos casos de teste incluídos. O método e os testes que escrevi dependem da
FilenameUtils
classe do IO do Apache commons .A solução foi testada com Java 1.4. Se você estiver usando o Java 1.5 (ou superior), considere substituir
StringBuffer
porStringBuilder
(se ainda estiver usando o Java 1.4, considere uma mudança de empregador).Os casos de teste que são aprovados são
fonte
Ao usar o java.net.URI.relativize, você deve estar ciente do bug do Java: JDK-6226081 (o URI deve poder relativizar caminhos com raízes parciais)
O que essencialmente significa
java.net.URI.relativize
que não criará ".." para você.fonte
URIUtils.resolve()
menciona JDK-4708535. E do código fonte, não vejo nada relacionado ao retorno (ou seja,..
segmentos). Você confundiu os dois erros?No Java 7 e posterior, você pode simplesmente usar (e, ao contrário
URI
, é livre de erros):fonte
O bug referido em outra resposta é solucionado por URIUtils no Apache HttpComponents
fonte
Se você souber que a segunda string faz parte da primeira:
ou se você realmente deseja o período no início, como no seu exemplo:
fonte
A recursão produz uma solução menor. Isso gera uma exceção se o resultado for impossível (por exemplo, disco diferente do Windows) ou impraticável (raiz é apenas o diretório comum).
fonte
Aqui está uma solução gratuita para outras bibliotecas:
Saídas
[EDIT] na verdade ele gera mais .. \ por causa da origem do arquivo e não do diretório. A solução correta para o meu caso é:
fonte
Minha versão é vagamente baseada nas versões de Matt e Steve :
fonte
"/".length()
você deve usar separator.lengthA solução de Matt B faz com que o número de diretórios retorne errado - deve ser o comprimento do caminho base menos o número de elementos comuns do caminho, menos um (para o último elemento do caminho, que é um nome de arquivo ou um resultado
""
gerado porsplit
) . Acontece que ele trabalha com/a/b/c/
e/a/x/y/
, mas substitui os argumentos por/m/n/o/a/b/c/
e/m/n/o/a/x/y/
e você verá o problema.Além disso, ele precisa de um
else break
dentro do primeiro loop for, ou manipulará incorretamente os caminhos que possuem nomes de diretório correspondentes, como/a/b/c/d/
e/x/y/c/z
- oc
está no mesmo slot nas duas matrizes, mas não é uma correspondência real.Todas essas soluções não têm a capacidade de lidar com caminhos que não podem ser relativizados entre si porque possuem raízes incompatíveis, como
C:\foo\bar
eD:\baz\quux
. Provavelmente apenas um problema no Windows, mas vale a pena notar.Passei muito mais tempo nisso do que pretendia, mas tudo bem. Na verdade, eu precisava disso para trabalhar, por isso agradeço a todos que participaram, e tenho certeza de que também haverá correções nesta versão!
E aqui estão os testes para cobrir vários casos:
fonte
Na verdade, minha outra resposta não funcionaria se o caminho de destino não fosse filho do caminho base.
Isso deve funcionar.
fonte
Legal!! Eu preciso de um pouco de código como este, mas para comparar os caminhos de diretório em máquinas Linux. Descobri que isso não estava funcionando em situações em que um diretório pai era o alvo.
Aqui está uma versão amigável do diretório do método:
fonte
Suponho que você tenha fromPath (um caminho absoluto para uma pasta) e toPath (um caminho absoluto para uma pasta / arquivo), e você está procurando um caminho que represente o arquivo / pasta no toPath como um caminho relativo from fromPath (seu diretório de trabalho atual é fromPath ), algo assim deve funcionar:
fonte
Muitas respostas já estão aqui, mas descobri que elas não lidavam com todos os casos, como a base e o destino, sendo os mesmos. Essa função pega um diretório base e um caminho de destino e retorna o caminho relativo. Se não existir um caminho relativo, o caminho de destino será retornado. File.separator é desnecessário.
fonte
Aqui, um método que resolve um caminho relativo a partir de um caminho base, independentemente de estarem na mesma raiz ou em uma raiz diferente:
fonte
Passa nos testes de Dónal, a única mudança - se nenhuma raiz comum retorna o caminho de destino (já pode ser relativo)
fonte
Se você estiver escrevendo um plug-in Maven, poderá usar o Plexus '
PathTool
:fonte
Se Paths não estiver disponível para o tempo de execução do JRE 1.5 ou o plug-in maven
fonte
org.apache.ant possui uma classe FileUtils com um método getRelativePath. Ainda não tentei, mas poderia valer a pena conferir.
http://javadoc.haefelinger.it/org.apache.ant/1.7.1/org/apache/tools/ant/util/FileUtils.html#getRelativePath(java.io.File , java.io.File)
fonte
fonte
Código Psuedo:
return "." + whicheverPathIsLonger.substring(commonPath.length);
fonte