Para Java 7 y versiones posteriores, consulte la respuesta de @ VitaliiFedorenko.
Andy Thomas
1
tl; dr respuesta: Paths.get (startPath) .relativize (Paths.get (endPath)). toString () (que, por cierto, parece estar funcionando bien con, por ejemplo, "../" para mí en Java 8 , entonces ...)
Andrew
Respuestas:
298
Es una pequeña rotonda, pero ¿por qué no usar URI? Tiene un método de relativización que realiza todas las comprobaciones necesarias por usted.
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"
Tenga en cuenta que para la ruta del archivo hay java.nio.file.Path#relativizedesde Java 1.7, como señaló @Jirka Meluzin en la otra respuesta .
Ver la respuesta de Peter Mueller. relativize () parece bastante roto para todos menos los casos más simples.
Dave Ray
11
Sí, solo funciona si la ruta base es un padre de la primera ruta. Si necesita algo jerárquico hacia atrás como "../../relativepath", no funcionará. Encontré una solución: mrpmorris.blogspot.com/2007/05/…
Aurelien Ribon
44
Como escribió @VitaliiFedorenko: use java.nio.file.Path#relativize(Path), solo funciona con los puntos dobles primarios y todo.
Campa
Considere usar en toPath()lugar de toURI(). Es perfectamente capaz de crear cosas como "..\..". Pero tenga en cuenta la java.lang.IllegalArgumentException: 'other' has different rootexcepción cuando solicite una ruta relativa de "C:\temp"a "D:\temp".
Igor
Esto no funciona como se esperaba, devuelve data / stuff / xyz.dat en mi caso de prueba.
Agradable, corto, sin lib extra1. La solución de Adam Crume (hit 1) no pasa mis pruebas y la siguiente respuesta (hit2) "The Only 'Working' Solution" agrega un nuevo jar Y es más código que mi implementación, encuentro esto aquí después ... mejor que nunca .- )
Se verificó que esto maneja agregar ..cuando sea necesario (lo hace).
Owen
Desafortunadamente, Android no incluye java.nio.file:(
Nathan Osman
1
Descubrí que obtienes resultados extraños si el "pathBase" no está "normalizado" antes de "relativizar". Aunque está bien en este ejemplo, lo haría pathBase.normalize().relativize(pathAbsolute);como regla general.
pstanton 01 de
77
Al momento de escribir (junio de 2010), esta era la única solución que superó mis casos de prueba. No puedo garantizar que esta solución esté libre de errores, pero sí pasa los casos de prueba incluidos. El método y las pruebas que he escrito dependen de la FilenameUtilsclase de Apache commons IO .
La solución se probó con Java 1.4. Si está utilizando Java 1.5 (o superior) debe considerar la sustitución StringBuffercon StringBuilder(si usted todavía está utilizando Java 1.4 se debe considerar un cambio de patrón en su lugar).
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);}}}
Los casos de prueba que esto pasa son
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}}
¡Agradable! Sin embargo, una cosa se rompe si la base y el objetivo son iguales: la cadena común termina en un separador, que la ruta de destino normalizada no tiene, por lo que la llamada de subcadena solicita demasiados dígitos. Creo que lo arreglé agregando lo siguiente antes de las dos últimas líneas de la función: if (common.length ()> = normalizedTargetPath.length ()) {return "."; }
Erhannis
44
Decir que esta es la única solución de trabajo es engañoso. Otras respuestas funcionan mejor (esta respuesta se bloquea cuando la base y el objetivo son iguales), son más simples y no dependen de commons-io.
Paths.get (startPath) .relativize (Paths.get (endPath)). ToString () parece estar funcionando bien con, por ejemplo, "../" para mí en Java 8.
Andrew
@skaffman, ¿estás seguro? Esta respuesta hace referencia al error JDK-6226081, pero URIUtils.resolve()menciona JDK-4708535. Y a partir del código fuente, no veo nada relacionado con el retroceso (es decir, ..segmentos). ¿Confundiste los dos bichos?
Garret Wilson
JDK-6920138 está marcado como un duplicado de JDK-4708535.
Christian K.
18
En Java 7 y versiones posteriores, simplemente puede usar (y, en contraste URI, está libre de errores):
Cadena s3 = "." + s1.substring (s2.length ()); es un poco más legible IMO
Dónal
10
La recursión produce una solución más pequeña. Esto arroja una excepción si el resultado es imposible (por ejemplo, un disco de Windows diferente) o poco práctico (la raíz es solo un directorio común).
/**
* 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));}}
getCanonicalPath puede ser pesado, por lo que esta solución no puede recomendarse cuando necesita procesar cientos de miles de registros. Por ejemplo, tengo algunos archivos de listado que tienen hasta un millón de registros y ahora quiero moverlos para usar la ruta relativa para la portabilidad.
Mi versión se basa libremente en las versiones de Matt y 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 mí. Solo corrección menor: en lugar de "/".length()usar debería usar separator.length
leonbloy
5
La solución de Matt B obtiene el número de directorios para retroceder incorrectamente: debe ser la longitud de la ruta base menos el número de elementos de ruta comunes, menos uno (para el último elemento de ruta, que es un nombre de archivo o un seguimiento ""generado por split) . Resulta que funciona con /a/b/c/y /a/x/y/, pero reemplaza los argumentos con /m/n/o/a/b/c/y/m/n/o/a/x/y/ y verás el problema.
Además, necesita un else breakbucle for first first, o manejará mal las rutas que tienen nombres de directorio coincidentes, como /a/b/c/d/y /x/y/c/z- thec está en la misma ranura en ambas matrices, pero no es una coincidencia real.
Todas estas soluciones carecen de la capacidad de manejar caminos que no pueden relativizarse entre sí porque tienen raíces incompatibles, como C:\foo\bary D:\baz\quux. Probablemente solo sea un problema en Windows, pero vale la pena señalarlo.
Pasé mucho más tiempo de lo que pretendía, pero está bien. De hecho, necesitaba esto para el trabajo, así que gracias a todos los que intervinieron, ¡y estoy seguro de que también habrá correcciones en esta versión!
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;}
Y aquí hay pruebas para cubrir varios 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);}
En realidad, mi otra respuesta no funcionó si la ruta de destino no era hija de la ruta base.
Esto debería 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);}}
En lugar de File.pathSeparator debería ser File.separator. pathSeparator solo debe usarse para split (regex), ya que para "////" regex (win path regex), la ruta de resultado será incorrecta.
Alex Ivasyuv
3
¡¡Frio!! Necesito un poco de código como este, pero para comparar rutas de directorio en máquinas Linux. Descubrí que esto no funcionaba en situaciones en las que el directorio principal era el objetivo.
Aquí hay una versión de directorio amigable del 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;}
Supongo que tiene fromPath (una ruta absoluta para una carpeta) y toPath (una ruta absoluta para una carpeta / archivo), y está buscando una ruta que represente el archivo / carpeta en toPath como una ruta relativa from fromPath (su directorio de trabajo actual es fromPath ), entonces algo como esto debería 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();}
Muchas respuestas ya están aquí, pero descubrí que no manejan todos los casos, como que la base y el objetivo son los mismos. Esta función toma un directorio base y una ruta de destino y devuelve la ruta relativa. Si no existe una ruta relativa, se devuelve la ruta de destino. File.separator es innecesario.
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();}
Aquí, un método que resuelve una ruta relativa desde una ruta base independientemente de si están en la misma raíz o en una raíz 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;}
Dividir las cadenas por el separador de ruta ("/")
Encuentre la ruta común más grande iterando a través del resultado de la cadena dividida (para que termine con "/ var / data" o "/ a" en sus dos ejemplos)
Respuestas:
Es una pequeña rotonda, pero ¿por qué no usar URI? Tiene un método de relativización que realiza todas las comprobaciones necesarias por usted.
Tenga en cuenta que para la ruta del archivo hay
java.nio.file.Path#relativize
desde Java 1.7, como señaló @Jirka Meluzin en la otra respuesta .fuente
java.nio.file.Path#relativize(Path)
, solo funciona con los puntos dobles primarios y todo.toPath()
lugar detoURI()
. Es perfectamente capaz de crear cosas como"..\.."
. Pero tenga en cuenta lajava.lang.IllegalArgumentException: 'other' has different root
excepción cuando solicite una ruta relativa de"C:\temp"
a"D:\temp"
.Desde Java 7 puede usar el método relativize :
Salida:
fuente
..
cuando sea necesario (lo hace).java.nio.file
:(pathBase.normalize().relativize(pathAbsolute);
como regla general.Al momento de escribir (junio de 2010), esta era la única solución que superó mis casos de prueba. No puedo garantizar que esta solución esté libre de errores, pero sí pasa los casos de prueba incluidos. El método y las pruebas que he escrito dependen de la
FilenameUtils
clase de Apache commons IO .La solución se probó con Java 1.4. Si está utilizando Java 1.5 (o superior) debe considerar la sustitución
StringBuffer
conStringBuilder
(si usted todavía está utilizando Java 1.4 se debe considerar un cambio de patrón en su lugar).Los casos de prueba que esto pasa son
fuente
Cuando utilice java.net.URI.relativize, debe tener en cuenta el error de Java: JDK-6226081 (URI debería poder relativizar rutas con raíces parciales)
Lo que esencialmente significa
java.net.URI.relativize
que no creará ".." para ti.fuente
URIUtils.resolve()
menciona JDK-4708535. Y a partir del código fuente, no veo nada relacionado con el retroceso (es decir,..
segmentos). ¿Confundiste los dos bichos?En Java 7 y versiones posteriores, simplemente puede usar (y, en contraste
URI
, está libre de errores):fuente
El error mencionado en otra respuesta es abordado por URIUtils en Apache HttpComponents
fuente
Si sabe que la segunda cadena es parte de la primera:
o si realmente quieres el punto al principio como en tu ejemplo:
fuente
La recursión produce una solución más pequeña. Esto arroja una excepción si el resultado es imposible (por ejemplo, un disco de Windows diferente) o poco práctico (la raíz es solo un directorio común).
fuente
Aquí hay una solución libre de otra biblioteca:
Salidas
[EDITAR] en realidad sale en más .. \ porque la fuente es el archivo, no un directorio. La solución correcta para mi caso es:
fuente
Mi versión se basa libremente en las versiones de Matt y Steve :
fuente
"/".length()
usar debería usar separator.lengthLa solución de Matt B obtiene el número de directorios para retroceder incorrectamente: debe ser la longitud de la ruta base menos el número de elementos de ruta comunes, menos uno (para el último elemento de ruta, que es un nombre de archivo o un seguimiento
""
generado porsplit
) . Resulta que funciona con/a/b/c/
y/a/x/y/
, pero reemplaza los argumentos con/m/n/o/a/b/c/
y/m/n/o/a/x/y/
y verás el problema.Además, necesita un
else break
bucle for first first, o manejará mal las rutas que tienen nombres de directorio coincidentes, como/a/b/c/d/
y/x/y/c/z
- thec
está en la misma ranura en ambas matrices, pero no es una coincidencia real.Todas estas soluciones carecen de la capacidad de manejar caminos que no pueden relativizarse entre sí porque tienen raíces incompatibles, como
C:\foo\bar
yD:\baz\quux
. Probablemente solo sea un problema en Windows, pero vale la pena señalarlo.Pasé mucho más tiempo de lo que pretendía, pero está bien. De hecho, necesitaba esto para el trabajo, así que gracias a todos los que intervinieron, ¡y estoy seguro de que también habrá correcciones en esta versión!
Y aquí hay pruebas para cubrir varios casos:
fuente
En realidad, mi otra respuesta no funcionó si la ruta de destino no era hija de la ruta base.
Esto debería funcionar.
fuente
¡¡Frio!! Necesito un poco de código como este, pero para comparar rutas de directorio en máquinas Linux. Descubrí que esto no funcionaba en situaciones en las que el directorio principal era el objetivo.
Aquí hay una versión de directorio amigable del método:
fuente
Supongo que tiene fromPath (una ruta absoluta para una carpeta) y toPath (una ruta absoluta para una carpeta / archivo), y está buscando una ruta que represente el archivo / carpeta en toPath como una ruta relativa from fromPath (su directorio de trabajo actual es fromPath ), entonces algo como esto debería funcionar:
fuente
Muchas respuestas ya están aquí, pero descubrí que no manejan todos los casos, como que la base y el objetivo son los mismos. Esta función toma un directorio base y una ruta de destino y devuelve la ruta relativa. Si no existe una ruta relativa, se devuelve la ruta de destino. File.separator es innecesario.
fuente
Aquí, un método que resuelve una ruta relativa desde una ruta base independientemente de si están en la misma raíz o en una raíz diferente:
fuente
Pasa las pruebas de Dónal, el único cambio: si no hay una raíz común, devuelve la ruta de destino (ya podría ser relativa)
fuente
Si está escribiendo un complemento Maven, puede usar Plexus '
PathTool
:fuente
Si Paths no está disponible para JRE 1.5 runtime o plugin maven
fuente
org.apache.ant tiene una clase FileUtils con un método getRelativePath. Todavía no lo he probado, pero podría valer la pena echarle un vistazo.
http://javadoc.haefelinger.it/org.apache.ant/1.7.1/org/apache/tools/ant/util/FileUtils.html#getRelativePath(java.io.File , java.io.File)
fuente
fuente
Código Psuedo:
return "." + whicheverPathIsLonger.substring(commonPath.length);
fuente