给出两条绝对路径,例如
/var/data/stuff/xyz.dat
/var/data
如何创建一个以第二条路径为基础的相对路径?在上面的例子中,结果应该是:./stuff/xyz.dat
给出两条绝对路径,例如
/var/data/stuff/xyz.dat
/var/data
如何创建一个以第二条路径为基础的相对路径?在上面的例子中,结果应该是:./stuff/xyz.dat
当前回答
这里有一个其他库免费的解决方案:
Path sourceFile = Paths.get("some/common/path/example/a/b/c/f1.txt");
Path targetFile = Paths.get("some/common/path/example/d/e/f2.txt");
Path relativePath = sourceFile.relativize(targetFile);
System.out.println(relativePath);
输出
..\..\..\..\d\e\f2.txt
[编辑]实际上它输出更多。因为源文件不是目录。对于我的情况,正确的解决方案是:
Path sourceFile = Paths.get(new File("some/common/path/example/a/b/c/f1.txt").parent());
Path targetFile = Paths.get("some/common/path/example/d/e/f2.txt");
Path relativePath = sourceFile.relativize(targetFile);
System.out.println(relativePath);
其他回答
如果你正在编写一个Maven插件,你可以使用Plexus的PathTool:
import org.codehaus.plexus.util.PathTool;
String relativeFilePath = PathTool.getRelativeFilePath(file1, file2);
通过Dónal的测试,唯一的变化-如果没有公共根,它将返回目标路径(它可能已经是相对的)
import static java.util.Arrays.asList;
import static java.util.Collections.nCopies;
import static org.apache.commons.io.FilenameUtils.normalizeNoEndSeparator;
import static org.apache.commons.io.FilenameUtils.separatorsToUnix;
import static org.apache.commons.lang3.StringUtils.getCommonPrefix;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
import static org.apache.commons.lang3.StringUtils.join;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class ResourceUtils {
public static String getRelativePath(String targetPath, String basePath, String pathSeparator) {
File baseFile = new File(basePath);
if (baseFile.isFile() || !baseFile.exists() && !basePath.endsWith("/") && !basePath.endsWith("\\"))
basePath = baseFile.getParent();
String target = separatorsToUnix(normalizeNoEndSeparator(targetPath));
String base = separatorsToUnix(normalizeNoEndSeparator(basePath));
String commonPrefix = getCommonPrefix(target, base);
if (isBlank(commonPrefix))
return targetPath.replaceAll("/", pathSeparator);
target = target.replaceFirst(commonPrefix, "");
base = base.replaceFirst(commonPrefix, "");
List<String> result = new ArrayList<>();
if (isNotEmpty(base))
result.addAll(nCopies(base.split("/").length, ".."));
result.addAll(asList(target.replaceFirst("^/", "").split("/")));
return join(result, pathSeparator);
}
}
递归产生一个较小的解决方案。如果结果不可能(例如不同的Windows磁盘)或不切实际(根目录只是普通目录),则抛出异常。
/**
* 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:\"
*/
public static String getRelativePath(File base, File name) throws IOException {
File parent = base.getParentFile();
if (parent == null) {
throw new IOException("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));
}
}
这里有一个其他库免费的解决方案:
Path sourceFile = Paths.get("some/common/path/example/a/b/c/f1.txt");
Path targetFile = Paths.get("some/common/path/example/d/e/f2.txt");
Path relativePath = sourceFile.relativize(targetFile);
System.out.println(relativePath);
输出
..\..\..\..\d\e\f2.txt
[编辑]实际上它输出更多。因为源文件不是目录。对于我的情况,正确的解决方案是:
Path sourceFile = Paths.get(new File("some/common/path/example/a/b/c/f1.txt").parent());
Path targetFile = Paths.get("some/common/path/example/d/e/f2.txt");
Path relativePath = sourceFile.relativize(targetFile);
System.out.println(relativePath);
这有点迂回,但为什么不使用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。正如@Jirka Meluzin在另一个答案中指出的那样,自Java 1.7以来,路径#相对化。