예를 들어 두 개의 절대 경로
/var/data/stuff/xyz.dat
/var/data
두 번째 경로를 기본으로 사용하는 상대 경로를 어떻게 만들 수 있습니까? 위의 예에서 결과는 다음과 같아야합니다../stuff/xyz.dat
답변
약간 우회하지만 URI를 사용하지 않는 이유는 무엇입니까? 여기에는 필요한 모든 검사를 수행하는 상대화 방법이 있습니다.
String path = "/var/data/stuff/xyz.dat";
String base = "/var/data";
String relative = new File(base).toURI().relativize(new File(path).toURI()).getPath();
// relative == "stuff/xyz.dat"
파일 경로가 있음을 유의하시기 바랍니다 java.nio.file.Path#relativize
가리키는 아웃로, 자바 1.7 이후 @Jirka Meluzin 에 다른 대답 .
답변
Java 7부터 relativize 메소드를 사용할 수 있습니다 .
import java.nio.file.Path;
import java.nio.file.Paths;
public class Test {
public static void main(String[] args) {
Path pathAbsolute = Paths.get("/var/data/stuff/xyz.dat");
Path pathBase = Paths.get("/var/data");
Path pathRelative = pathBase.relativize(pathAbsolute);
System.out.println(pathRelative);
}
}
산출:
stuff/xyz.dat
답변
글을 쓰는 시점 (2010 년 6 월)은 이것이 테스트 사례를 통과 한 유일한 솔루션이었습니다. 이 솔루션에 버그가 없음을 보장 할 수는 없지만 포함 된 테스트 사례를 통과합니다. 필자가 작성한 방법과 테스트 FilenameUtils
는 Apache commons IO 의 클래스에 따라 다릅니다 .
솔루션은 Java 1.4로 테스트되었습니다. 당신은 당신이 교체를 고려해야합니다 (더 이상) 자바 1.5를 사용하는 경우 StringBuffer
에 StringBuilder
(당신은 여전히 자바 1.4을 사용하는 경우 당신은 고용주의 변화 대신을 고려해야한다).
import java.io.File;
import java.util.regex.Pattern;
import org.apache.commons.io.FilenameUtils;
public class ResourceUtils {
/**
* 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
*/
public static String getRelativePath(String targetPath, String basePath, String pathSeparator) {
// Normalize the paths
String normalizedTargetPath = FilenameUtils.normalizeNoEndSeparator(targetPath);
String normalizedBasePath = FilenameUtils.normalizeNoEndSeparator(basePath);
// Undo the changes to the separators made by normalization
if (pathSeparator.equals("/")) {
normalizedTargetPath = FilenameUtils.separatorsToUnix(normalizedTargetPath);
normalizedBasePath = FilenameUtils.separatorsToUnix(normalizedBasePath);
} else if (pathSeparator.equals("\\")) {
normalizedTargetPath = FilenameUtils.separatorsToWindows(normalizedTargetPath);
normalizedBasePath = FilenameUtils.separatorsToWindows(normalizedBasePath);
} else {
throw new IllegalArgumentException("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 = new StringBuffer();
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.
throw new PathResolutionException("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 do
boolean baseIsFile = true;
File baseResource = new File(normalizedBasePath);
if (baseResource.exists()) {
baseIsFile = baseResource.isFile();
} else if (basePath.endsWith(pathSeparator)) {
baseIsFile = false;
}
StringBuffer relative = new StringBuffer();
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();
}
static class PathResolutionException extends RuntimeException {
PathResolutionException(String msg) {
super(msg);
}
}
}
이것이 통과 한 테스트 사례는
public void 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/", "/"));
}
public void 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);
}
public void 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);
}
public void 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);
}
public void testGetRelativePathDirectoryToDirectory() {
String target = "C:\\Windows\\Boot\\";
String base = "C:\\Windows\\Speech\\Common\\";
String expected = "..\\..\\Boot";
String relPath = ResourceUtils.getRelativePath(target, base, "\\");
assertEquals(expected, relPath);
}
public void 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
}
}
답변
java.net.URI.relativize를 사용할 때 Java 버그를 알고 있어야합니다.
JDK-6226081 (URI는 부분 루트가있는 경로를 상대화 할 수 있어야합니다)
현재의
relativize()
방법은URI
하나가 다른 것의 접두사 일 때만 URI를 상대화합니다.
본질적으로 java.net.URI.relativize
“..”를 만들지 않습니다.
답변
다른 답변 에서 언급 된 버그 는 Apache HttpComponents의 URIUtils 로 해결됩니다.
public static URI resolve(URI baseURI,
String reference)
기본 URI에 대한 URI 참조를 해결합니다. java.net.URI ()의 버그에 대한 해결 방법
답변
Java 7 이상 에서는 간단히 사용할 수 있습니다 (와 달리 URI
버그가 없음).
Path#relativize(Path)
답변
두 번째 문자열이 첫 번째 문자열의 일부라는 것을 알고 있다면 :
String s1 = "/var/data/stuff/xyz.dat";
String s2 = "/var/data";
String s3 = s1.substring(s2.length());
또는 예제 에서처럼 처음에 마침표를 원한다면 :
String s3 = ".".concat(s1.substring(s2.length()));