Как построить относительный путь в Java из двух абсолютных путей (или URL)?
275
Даны два абсолютных пути, например
/var/data/stuff/xyz.dat/var/data
Как можно создать относительный путь, который использует второй путь в качестве своей базы? В приведенном выше примере результат должен быть:./stuff/xyz.dat
Для Java 7 и более поздних версий смотрите ответ @ VitaliiFedorenko.
Энди Томас
1
tl; dr ответ: Paths.get (startPath) .relativize (Paths.get (endPath)). toString () (который, кстати, вроде бы работает нормально, например, "../" для меня в Java 8 итак ...)
Андрей
Ответы:
298
Это немного окольным путем, но почему бы не использовать URI? У него есть метод релятивизации, который делает все необходимые проверки для вас.
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"
Обратите внимание, что для пути к файлу есть java.nio.file.Path#relativizeначиная с Java 1.7, как указал @Jirka Meluzin в другом ответе .
Смотрите ответ Питера Мюллера. релятивизация () выглядит довольно неработоспособной для всех, кроме самых простых случаев.
Дейв Рэй
11
Да, это работает, только если базовый путь является родителем первого пути. Если вам нужна иерархическая обратная связь, например "../../relativepath", это не сработает. Я нашел решение: mrpmorris.blogspot.com/2007/05/…
Рибон,
4
Как писал @VitaliiFedorenko: используйте java.nio.file.Path#relativize(Path), он просто работает с родительскими двойными точками и все.
Кампа
Попробуйте использовать toPath()вместо toURI(). Он прекрасно умеет создавать такие вещи, как "..\..". Но помните об java.lang.IllegalArgumentException: 'other' has different rootисключении при запросе относительного пути от "C:\temp"к "D:\temp".
Игорь
Это не работает должным образом, в моем тестовом случае он возвращает data / stuff / xyz.dat.
unbekant
238
Начиная с Java 7 вы можете использовать метод релятивизации :
Хороший, короткий, без лишних lib +1. Решение Адама Крума (хит 1) не проходит мои тесты, и следующий ответ (хит2) «Единственное« рабочее »решение» добавляет новый jar-файл и больше кода, чем моя реализация, я нахожу это здесь потом ... лучше, чем никогда .- )
Проверено, что это обрабатывает добавление, ..где необходимо (это делает).
Оуэн
К сожалению, Android не включает java.nio.file:(
Натан Осман
1
Я обнаружил, что вы получите странные результаты, если «pathBase» не «нормализован» до «релятивизировать». Хотя хорошо в этом примере, я бы сделал pathBase.normalize().relativize(pathAbsolute);как общее правило.
pstanton
77
На момент написания статьи (июнь 2010 года) это было единственное решение, которое прошло мои тестовые случаи. Я не могу гарантировать, что это решение не содержит ошибок, но оно проходит тестовые примеры. Методы и тесты, которые я написал, зависят от FilenameUtilsкласса Apache commons IO .
Решение было протестировано с Java 1.4. Если вы используете Java 1.5 (или выше), вам следует подумать о замене StringBufferна StringBuilder(если вы все еще используете Java 1.4, вам следует рассмотреть возможность смены работодателя).
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);}}}
Тестовые случаи, что это проходит
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}}
Ницца! Одна вещь, однако, ломается, если основание и цель одинаковы - строка общего должна заканчиваться разделителем, которого нет в нормализованном целевом пути, поэтому вызов подстроки запрашивает слишком много цифр. Думаю, я исправил это, добавив следующее перед двумя последними строками функции: if (common.length ()> = normalizedTargetPath.length ()) {return "."; }
Эрханнис
4
Сказать, что это единственное рабочее решение, вводит в заблуждение. Другие ответы работают лучше (этот ответ дает сбой, когда база и цель совпадают), проще и не зависят от commons-io.
Paths.get (startPath) .relativize (Paths.get (endPath)). ToString (), кажется, работает очень хорошо, например, "../" для меня в Java 8.
Эндрю
@skaffman ты уверен? Этот ответ ссылается на ошибку JDK-6226081, но URIUtils.resolve()упоминает JDK-4708535. И из исходного кода я не вижу ничего, связанного с возвратом (то есть ..сегменты). Вы перепутали две ошибки?
Гаррет Уилсон
JDK-6920138 помечен как дубликат JDK-4708535.
Кристиан К.
18
В Java 7 и более поздних версиях вы можете просто использовать (и, в отличие от этого URI, он не содержит ошибок):
Строка s3 = "." + s1.substring (s2.length ()); немного более читабелен ИМО
Донал
10
Рекурсия дает меньшее решение. Это вызывает исключение, если результат невозможен (например, другой диск Windows) или нецелесообразен (root - это всего лишь общий каталог).
/**
* 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 может иметь большой вес, поэтому это решение не рекомендуется, если вам нужно обработать сотни тысяч записей. Например, у меня есть несколько файлов списков, содержащих до миллиона записей, и теперь я хочу переместить их, чтобы использовать относительный путь для переносимости.
Моя версия свободно основана на версиях Мэтта и Стива :
/**
* 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 работает на меня. Только незначительное исправление: вместо "/".length()вас следует использовать separator.length
leonbloy
5
Решение Matt B возвращает количество каталогов для обратного отслеживания неправильно - это должна быть длина базового пути минус количество элементов общего пути, минус один (для последнего элемента пути, который является либо именем файла, либо завершающим ""сгенерированным split) , Это работает с /a/b/c/и /a/x/y/, но замените аргументы на /m/n/o/a/b/c/и, /m/n/o/a/x/y/и вы увидите проблему.
Кроме того, ему нужен else breakвнутренний цикл for, первый, или он будет неправильно обрабатывать пути, в которых есть совпадающие имена каталогов, такие как /a/b/c/d/и /x/y/c/z- cнаходится в одном и том же слоте в обоих массивах, но не является фактическим соответствием.
Все эти решения не способны обрабатывать пути, которые нельзя соотнести друг с другом, потому что они имеют несовместимые корни, такие как C:\foo\barи D:\baz\quux. Вероятно, проблема только в Windows, но стоит отметить.
Я потратил на это гораздо больше времени, чем хотел, но ничего страшного. Я действительно нуждался в этом для работы, так что спасибо всем, кто присоединился, и я уверен, что будут исправления и к этой версии!
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;}
И вот тесты, чтобы покрыть несколько случаев:
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);}
На самом деле мой другой ответ не сработал, если целевой путь не был дочерним по отношению к базовому пути.
Это должно работать.
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);}}
Вместо File.pathSeparator должен быть File.separator. pathSeparator следует использовать только для разбиения (регулярное выражение), так как для регулярного выражения "////" (регулярное выражение win path) путь результата будет неверным.
Алекс Ивасёв
3
Прохладно!! Мне нужно немного такого кода, но для сравнения путей к каталогам на машинах Linux. Я обнаружил, что это не работает в ситуациях, когда целью является родительский каталог.
Вот справочная версия метода:
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;}
Я предполагаю, что у вас есть fromPath (абсолютный путь к папке) и toPath (абсолютный путь к папке / файлу), и вы ищете путь, который представляет файл / папку в toPath как относительный путь from fromPath (ваш текущий рабочий каталог - fromPath ), тогда что-то вроде этого должно работать:
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();}
Множество ответов уже здесь, но я обнаружил, что они не обрабатывают все случаи, такие как основание и цель одинаковы. Эта функция берет базовый каталог и целевой путь и возвращает относительный путь. Если относительный путь не существует, целевой путь возвращается. File.separator не нужен.
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();}
Здесь метод, который разрешает относительный путь из базового пути, независимо от того, находятся ли они в том же или в другом корне:
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;}
Найдите наибольший общий путь путем итерации по результату строки разбиения (так что в итоге вы получите «/ var / data» или «/ a» в ваших двух примерах)
Ответы:
Это немного окольным путем, но почему бы не использовать URI? У него есть метод релятивизации, который делает все необходимые проверки для вас.
Обратите внимание, что для пути к файлу есть
java.nio.file.Path#relativize
начиная с Java 1.7, как указал @Jirka Meluzin в другом ответе .источник
java.nio.file.Path#relativize(Path)
, он просто работает с родительскими двойными точками и все.toPath()
вместоtoURI()
. Он прекрасно умеет создавать такие вещи, как"..\.."
. Но помните обjava.lang.IllegalArgumentException: 'other' has different root
исключении при запросе относительного пути от"C:\temp"
к"D:\temp"
.Начиная с Java 7 вы можете использовать метод релятивизации :
Вывод:
источник
..
где необходимо (это делает).java.nio.file
:(pathBase.normalize().relativize(pathAbsolute);
как общее правило.На момент написания статьи (июнь 2010 года) это было единственное решение, которое прошло мои тестовые случаи. Я не могу гарантировать, что это решение не содержит ошибок, но оно проходит тестовые примеры. Методы и тесты, которые я написал, зависят от
FilenameUtils
класса Apache commons IO .Решение было протестировано с Java 1.4. Если вы используете Java 1.5 (или выше), вам следует подумать о замене
StringBuffer
наStringBuilder
(если вы все еще используете Java 1.4, вам следует рассмотреть возможность смены работодателя).Тестовые случаи, что это проходит
источник
При использовании java.net.URI.relativize вы должны знать об ошибке Java: JDK-6226081 (URI должен иметь возможность релятивизировать пути с частичными корнями)
Что по существу означает,
java.net.URI.relativize
что не создаст ".." для вас.источник
URIUtils.resolve()
упоминает JDK-4708535. И из исходного кода я не вижу ничего, связанного с возвратом (то есть..
сегменты). Вы перепутали две ошибки?В Java 7 и более поздних версиях вы можете просто использовать (и, в отличие от этого
URI
, он не содержит ошибок):источник
Ошибка, упомянутая в другом ответе , устранена URIUtils в Apache HttpComponents
источник
Если вы знаете, что вторая строка является частью первой:
или если вы действительно хотите точку в начале, как в вашем примере:
источник
Рекурсия дает меньшее решение. Это вызывает исключение, если результат невозможен (например, другой диск Windows) или нецелесообразен (root - это всего лишь общий каталог).
источник
Вот решение другой библиотеки бесплатно:
Выходы
[EDIT] фактически выводит больше .. \ потому что источником является файл, а не каталог. Правильное решение для моего случая:
источник
Моя версия свободно основана на версиях Мэтта и Стива :
источник
"/".length()
вас следует использовать separator.lengthРешение Matt B возвращает количество каталогов для обратного отслеживания неправильно - это должна быть длина базового пути минус количество элементов общего пути, минус один (для последнего элемента пути, который является либо именем файла, либо завершающим
""
сгенерированнымsplit
) , Это работает с/a/b/c/
и/a/x/y/
, но замените аргументы на/m/n/o/a/b/c/
и,/m/n/o/a/x/y/
и вы увидите проблему.Кроме того, ему нужен
else break
внутренний цикл for, первый, или он будет неправильно обрабатывать пути, в которых есть совпадающие имена каталогов, такие как/a/b/c/d/
и/x/y/c/z
-c
находится в одном и том же слоте в обоих массивах, но не является фактическим соответствием.Все эти решения не способны обрабатывать пути, которые нельзя соотнести друг с другом, потому что они имеют несовместимые корни, такие как
C:\foo\bar
иD:\baz\quux
. Вероятно, проблема только в Windows, но стоит отметить.Я потратил на это гораздо больше времени, чем хотел, но ничего страшного. Я действительно нуждался в этом для работы, так что спасибо всем, кто присоединился, и я уверен, что будут исправления и к этой версии!
И вот тесты, чтобы покрыть несколько случаев:
источник
На самом деле мой другой ответ не сработал, если целевой путь не был дочерним по отношению к базовому пути.
Это должно работать.
источник
Прохладно!! Мне нужно немного такого кода, но для сравнения путей к каталогам на машинах Linux. Я обнаружил, что это не работает в ситуациях, когда целью является родительский каталог.
Вот справочная версия метода:
источник
Я предполагаю, что у вас есть fromPath (абсолютный путь к папке) и toPath (абсолютный путь к папке / файлу), и вы ищете путь, который представляет файл / папку в toPath как относительный путь from fromPath (ваш текущий рабочий каталог - fromPath ), тогда что-то вроде этого должно работать:
источник
Множество ответов уже здесь, но я обнаружил, что они не обрабатывают все случаи, такие как основание и цель одинаковы. Эта функция берет базовый каталог и целевой путь и возвращает относительный путь. Если относительный путь не существует, целевой путь возвращается. File.separator не нужен.
источник
Здесь метод, который разрешает относительный путь из базового пути, независимо от того, находятся ли они в том же или в другом корне:
источник
Проходит тесты Доналя, единственное изменение - если нет общего корня, он возвращает целевой путь (он может быть уже относительным)
источник
Если вы пишете плагин Maven, вы можете использовать Plexus '
PathTool
:источник
Если Paths недоступен для среды выполнения JRE 1.5 или плагина maven
источник
org.apache.ant имеет класс FileUtils с методом getRelativePath. Сам еще не пробовал, но стоит попробовать.
http://javadoc.haefelinger.it/org.apache.ant/1.7.1/org/apache/tools/ant/util/FileUtils.html#getRelativePath(java.io.File , java.io.File)
источник
источник
Псевдопользователей-код:
return "." + whicheverPathIsLonger.substring(commonPath.length);
источник