1. Overview
1.概述
In this tutorial, we’ll learn how to construct a relative path from two absolute paths in Java. We’ll focus on two built-in Java APIs – the new I/O (NIO2) Path API and the URI class.
在本教程中,我们将学习如何在Java中从两个绝对路径构建一个相对路径。我们将重点讨论两个内置的Java API–新的I/O(NIO2)路径API和URI类。
2. Absolute vs. Relative Paths
2.绝对路径与相对路径
Before we start, let’s do a quick recap. For all the examples in the text, we’ll use the same file structure in the user’s home directory:
在我们开始之前,让我们做一个简单的回顾。对于文中的所有例子,我们将在用户的主目录中使用相同的文件结构。
/ (root)
|-- baeldung
\-- bar
| |-- one.txt
| |-- two.txt
\-- foo
|-- three.txt
An absolute path describes a location regardless of the current working directory, starting at the root node. Here are the absolute paths for our files:
绝对路径描述了一个与当前工作目录无关的位置,从根节点开始。以下是我们文件的绝对路径。
one.txt -> /baeldung/bar/one.txt
two.txt -> /baeldung/bar/two.txt
three.txt -> /baeldung/foo/three.txt
The absolute paths always remain the same even if we change the working directory.
即使我们改变工作目录,绝对路径总是保持不变。
On the other hand, a relative path describes the location of the target node relative to its source. If we’re in the baeldung directory, let’s look at the relative paths for the files:
另一方面,相对路径描述了目标节点相对于其源节点的位置。如果我们在baeldung目录中,让我们看一下文件的相对路径。
one.txt -> ./bar/one.txt
two.txt -> ./bar/two.txt
three.txt -> ./foo/three.txt
Now, let’s move to the bar subdirectory and check the relative paths again:
现在,让我们移动到bar子目录,再次检查相对路径。
one.txt -> ./one.txt
two.txt -> ./two.txt
three.txt -> ../foo/three.txt
As we can see, the results are slightly different. We must remember that relative values can change if we modify the source context, while absolute paths are constant. An absolute path is a special case of a relative path, where the source node is the root of the system.
我们可以看到,结果略有不同。我们必须记住,如果我们修改源上下文,相对值会发生变化,而绝对路径是不变的。绝对路径是相对路径的一个特例,其中源节点是系统的根。
3. NIO2 API
3.NIO2 API
Now that we know how relative and absolute paths work, it’s time to check out the NIO2 API. As we know, the NIO2 API was introduced with the release of Java 7, and it improved the old I/O API, which had many pitfalls. Using this API, we’ll try to determine the relative path between two files described by their absolute paths.
现在我们知道了相对路径和绝对路径的工作原理,现在是时候查看NIO2 API了。正如我们所知,NIO2 API是随着Java 7的发布而引入的,它改进了旧的I/O API,该API有许多缺陷。使用这个API,我们将尝试确定由绝对路径描述的两个文件之间的相对路径。
Let’s start by constructing Path objects for our files:
让我们首先为我们的文件构建Path对象。
Path pathOne = Paths.get("/baeldung/bar/one.txt");
Path pathTwo = Paths.get("/baeldung/bar/two.txt");
Path pathThree = Paths.get("/baeldung/foo/three.txt");
To construct a relative path between the source and a given node, we can use the relativize(Path) method provided by the Path class:
为了在源节点和给定节点之间构建一个相对路径,我们可以使用Path类提供的relativize(Path)方法。
Path result = pathOne.relativize(pathTwo);
assertThat(result)
.isRelative()
.isEqualTo(Paths.get("../two.txt"));
As we see, the result is definitely a relative path. Is that correct? Especially with the parent operator (../) at the beginning?
正如我们看到的,结果肯定是一个相对路径。这样做正确吗?特别是开头的父运算符(…/)?
We must remember that a relative path can be specified starting from any type of node, which can be a directory or a file. Especially when we use CLI or explorers, we work with directories. Then all relative paths are calculated based on the current working directory.
我们必须记住,相对路径可以从任何类型的节点开始指定,它可以是一个目录或一个文件。特别是当我们使用CLI或explorer时,我们的工作对象是目录。那么所有的相对路径都是基于当前的工作目录来计算的。
In our example, we created the Path pointing to a specific file. So we first need to get to the parent of the file, its directory and then go to the second file. Overall, the result is correct.
在我们的例子中,我们创建了指向一个特定文件的Path。因此,我们首先需要进入文件的父级,即它的目录,然后再去找第二个文件。总的来说,结果是正确的。
If we want to make the result relative to the source directory, we can use the getParent() method:
如果我们想使结果相对于源目录,我们可以使用getParent()方法。
Path result = pathOne.getParent().relativize(pathTwo);
assertThat(result)
.isRelative()
.isEqualTo(Paths.get("two.txt"));
We should note that the Path object may point to any file or directory. We need to provide additional checks if we’re building a more complex logic.
我们应该注意,Path对象可以指向任何文件或目录。如果我们要建立一个更复杂的逻辑,我们需要提供额外的检查。
Finally, let’s check the relative path between the one.txt and three.txt files:
最后,让我们检查一下one.txt和three.txt文件之间的相对路径。
Path resultOneToThree = pathOne.relativize(pathThree);
Path resultThreeToOne = pathThree.relativize(pathOne);
assertThat(resultOneToThree)
.isRelative()
.isEqualTo(Paths.get("..\..\foo\three.txt"));
assertThat(result)
.isRelative()
.isEqualTo(Paths.get("..\..\bar\one.txt"));
This quick test confirms that the relative path is context-dependent. While the absolute paths are still the same, the relative path will be different when we swap the source and destination nodes together.
这个快速测试证实了相对路径是依赖于上下文的。虽然绝对路径仍然是一样的,但当我们把源节点和目的节点换到一起时,相对路径就会有所不同。
4. java.net.URI API
4、java.net.URI API
After checking the NIO2 API, let’s go to the java.net.URI class. We know that URI (Uniform Resource Identifier) is a string of characters that allows us to identify any resource that can also be used when working with files.
检查完NIO2的API后,让我们去看看java.net.URI类。我们知道,URI(统一资源标识符)是一串字符,允许我们识别任何资源,在处理文件时也可以使用。
Let’s construct URI objects for our files:
让我们为我们的文件构建URI对象。
URI uriOne = pathOne.toURI();
// URI uriOne = URI.create("file:///baeldung/bar/one.txt")
URI uriTwo = pathTwo.toURI();
URI uriThree = pathThree.toURI();
We can either construct a URI object using String or convert a previously created Path.
我们可以使用String构建一个URI对象,或者转换一个先前创建的Path。
As before, the URI class also provides a relativize(URI) method. Let’s use it to construct the relative path:
和以前一样,URI类也提供了一个relativize(URI)方法。让我们用它来构建相对路径。
URI result = uriOne.relativize(uriTwo);
assertThat(result)
.asString()
.contains("file:///baeldung/bar/two.txt");
The result isn’t what we expected, the relative path hasn’t been constructed correctly. To answer the question of why this is so, we need to check the official documentation of the class.
结果不是我们预期的那样,相对路径没有被正确构建。要回答为什么会这样的问题,我们需要查看该类的官方文档。
This method only returns a relative value if the source URI is a prefix of a target URI. Otherwise, it returns the target value. As a result, we are unable to build a relative path between the file nodes. In this scenario, one URI will never prefix the other.
只有当源URI是目标URI的前缀时,该方法才会返回一个相对值。否则,它会返回目标值。因此,我们无法在文件节点之间建立一个相对路径。在这种情况下,一个URI将永远不会是另一个的前缀。
To return a relative path, we can set our source URI as the directory of the first file:
为了返回一个相对路径,我们可以将我们的源URI设置为第一个文件的目录。
URI uriOneParent = pathOne.getParent().toUri(); // file:///baeldung/bar/
URI result = uriOneParent.relativize(uriTwo);
assertThat(result)
.asString()
.contains("two.txt");
Now the source node is the target prefix, so the result is calculated correctly. Due to the limitations of the method, we’re unable to determine the relative path between one.txt/two.txt and three.txt files using the URI method. Their directories will not have a common prefix.
现在,源节点是目标前缀,所以计算结果是正确的。由于方法的限制,我们无法用URI方法确定one.txt/two.txt和three.txt文件之间的相对路径。他们的目录不会有一个共同的前缀。
5. Summary
5.摘要
In this article, we started out by looking at the key differences between absolute and relative paths.
在这篇文章中,我们首先看了绝对路径和相对路径的主要区别。
Next, we constructed a relative path between two files described by their absolute paths. We started by checking the NIO2 API and detailed the relative path-building process.
接下来,我们在由绝对路径描述的两个文件之间构建一个相对路径。我们首先检查了NIO2的API,并详细介绍了相对路径的构建过程。
Finally, we tried to achieve the same result with the java.net.URI class. We found that we can’t do all transformations using this API due to its restrictions.
最后,我们试图用java.net.URI类来实现同样的结果。我们发现,由于这个API的限制,我们不能用它来完成所有的转换。
As always, all examples with the additional tests can be found over on GitHub.
像往常一样,所有带有额外测试的例子都可以在GitHub上找到。