文档

Java™ 教程-Java Tutorials 中文版
路径操作
Trail: Essential Classes
Lesson: Basic I/O
Section: File I/O (Featuring NIO.2)
Subsection: The Path Class

路径操作

Path 类包括各种方法,可用于获取有关路径的信息,访问路径元素,将路径转换为其他形式或提取路径部分。还存在用于匹配路径字符串的方法和用于去除路径中的冗余的方法。本课程介绍了这些 Path 方法,有时也称为 syntactic (语法) 操作,因为它们在路径上运行,不访问文件系统。

本节包括以下内容:

创建路径

Path 实例包含用于指定文件或目录位置的信息。在定义时,Path 提供一系列一个或多个名称。可能包含根元素或文件名,但两者都不是必需的。Path 可能只包含一个目录或文件名。

你可以使用 Paths (请注意复数)助手类中的以下 get 方法之一轻松创建 Path 对象:

Path p1 = Paths.get("/tmp/foo");
Path p2 = Paths.get(args[0]);
Path p3 = Paths.get(URI.create("file:///Users/joe/FileTest.java"));

Paths.get 方法是以下代码的简写:

Path p4 = FileSystems.getDefault().getPath("/users/sally");

下面的示例创建 /u/joe/logs/foo.log 假设你的主目录是 /u/joe,或 C:\joe\logs\foo.log 如果你在 Windows 上。

Path p5 = Paths.get(System.getProperty("user.home"),"logs", "foo.log");

获取有关路径的信息

你可以将 Path 视为将这些名称元素存储为序列。目录结构中的最高元素位于索引 0 处。目录结构中的最低元素位于索引 [n-1],其中 nPath 中的名称元素数。方法可用于使用这些索引获取单个元素或 Path 的子序列。

本课中的示例使用以下目录结构。

示例目录结构

示例目录结构

以下代码片段定义 Path 实例,然后调用多个方法以获取有关路径的信息:

// None of these methods requires that the file corresponding
// to the Path exists.
// Microsoft Windows syntax
Path path = Paths.get("C:\\home\\joe\\foo");

// Solaris syntax
Path path = Paths.get("/home/joe/foo");

System.out.format("toString: %s%n", path.toString());
System.out.format("getFileName: %s%n", path.getFileName());
System.out.format("getName(0): %s%n", path.getName(0));
System.out.format("getNameCount: %d%n", path.getNameCount());
System.out.format("subpath(0,2): %s%n", path.subpath(0,2));
System.out.format("getParent: %s%n", path.getParent());
System.out.format("getRoot: %s%n", path.getRoot());

以下是 Windows 和 Solaris OS 的输出:

方法调用 在 Solaris OS 中返回 在 Microsoft Windows 中返回 注释
toString /home/joe/foo C:\home\joe\foo 返回 Path 的字符串表示形式。如果路径是使用 Filesystems.getDefault().getPath(String)Paths.get 创建的(后者是 getPath 的便捷方法),该方法执行较小的语法清理。例如,在 UNIX 操作系统中,它会将输入字符串 //home/joe/foo 更正为 /home/joe/foo
getFileName foo foo 返回文件名或 name 元素序列的最后一个元素。
getName(0) home home 返回与指定索引对应的路径元素。第 0 个元素是最靠近根的路径元素。
getNameCount 3 3 返回路径中的元素数。
subpath(0,2) home/joe home\joe 返回由开始和结束索引指定的 Path(不包括根元素)的子序列。
getParent /home/joe \home\joe 返回父目录的路径。
getRoot / C:\ 返回路径的根。

前面的示例显示了绝对路径的输出。在以下示例中,指定了相对路径:

// Solaris syntax
Path path = Paths.get("sally/bar");
or
// Microsoft Windows syntax
Path path = Paths.get("sally\\bar");

以下是 Windows 和 Solaris OS 的输出:

方法调用 在 Solaris OS 中返回 在 Microsoft Windows 中返回
toString sally/bar sally\bar
getFileName bar bar
getName(0) sally sally
getNameCount 2 2
subpath(0,1) sally sally
getParent sally sally
getRoot null null

从路径中移除冗余

许多文件系统使用“.”符号表示当前目录,使用“..”表示父目录。你可能遇到 Path 包含冗余目录信息的情况。也许服务器配置为将其日志文件保存在“/dir/logs/.”目录中,并且你希望从路径中删除尾随的“/.”符号。

以下示例均包含冗余:

/home/./joe/foo
/home/sally/../joe/foo

normalize 方法移除所有冗余元素,包括任何“.”或“directory/..”匹配项。前面两个示例都标准化为 /home/joe/foo

重要的是要注意,normalize 在清理路径时不检查文件系统。这是一种纯粹的语法操作。在第二个示例中,如果 sally 是符号链接,则移除 sally/.. 可能会导致 Path 不再定位目标文件。

要在确保结果找到正确文件的同时清理路径,可以使用 toRealPath 方法。此方法将在下一节 Converting a Path 中介绍。

转换路径

你可以使用三种方法转换 Path。如果需要将路径转换为可以从浏览器打开的字符串,可以使用 toUri。例如:

Path p1 = Paths.get("/home/logfile");
// Result is file:///home/logfile
System.out.format("%s%n", p1.toUri());

toAbsolutePath 方法将路径转换为绝对路径。如果传入的路径已经是绝对路径,则返回相同的 Path 对象。处理用户输入的文件名时,toAbsolutePath 方法非常有用。例如:

public class FileTest {
    public static void main(String[] args) {

        if (args.length < 1) {
            System.out.println("usage: FileTest file");
            System.exit(-1);
        }

        // Converts the input string to a Path object.
        Path inputPath = Paths.get(args[0]);

        // Converts the input Path
        // to an absolute path.
        // Generally, this means prepending
        // the current working
        // directory.  If this example
        // were called like this:
        //     java FileTest foo
        // the getRoot and getParent methods
        // would return null
        // on the original "inputPath"
        // instance.  Invoking getRoot and
        // getParent on the "fullPath"
        // instance returns expected values.
        Path fullPath = inputPath.toAbsolutePath();
    }
}

toAbsolutePath 方法转换用户输入并返回 Path,在查询时返回有用的值。此方法无需存在该文件。

toRealPath 方法返回现有文件的 real 路径。此方法在一个中执行多个操作:

如果文件不存在或无法访问,则此方法将引发异常。当你想要处理任何这些情况时,你可以捕获异常。例如:

try {
    Path fp = path.toRealPath();
} catch (NoSuchFileException x) {
    System.err.format("%s: no such" + " file or directory%n", path);
    // Logic for case when file doesn't exist.
} catch (IOException x) {
    System.err.format("%s%n", x);
    // Logic for other sort of file error.
}

连接两条路径

你可以使用 resolve 方法组合路径。传入 partial path,这是一个不包含根元素的路径,并且该部分路径将附加到原始路径。

例如,请考虑以下代码段:

// Solaris
Path p1 = Paths.get("/home/joe/foo");
// Result is /home/joe/foo/bar
System.out.format("%s%n", p1.resolve("bar"));

or

// Microsoft Windows
Path p1 = Paths.get("C:\\home\\joe\\foo");
// Result is C:\home\joe\foo\bar
System.out.format("%s%n", p1.resolve("bar"));

将绝对路径传递给 resolve 方法将返回传入的路径:

// Result is /home/joe
Paths.get("foo").resolve("/home/joe");

在两条路径之间创建路径

编写文件 I/O 代码时的一个常见要求是能够构建从文件系统中的一个位置到另一个位置的路径。你可以使用 relativize 方法来满足此要求。此方法构造一个源自原始路径并在传入路径指定的位置结束的路径。新路径 relative (相对) 到原始路径。

例如,考虑定义为 joesally 的两个相对路径:

Path p1 = Paths.get("joe");
Path p2 = Paths.get("sally");

在没有任何其他信息的情况下,假设 joesally 是兄弟,意味着位于树结构中的同一级别的节点。要从 joe 导航到 sally,你可能希望先将一个级别导航到父节点,然后再导航到 sally

// Result is ../sally
Path p1_to_p2 = p1.relativize(p2);
// Result is ../joe
Path p2_to_p1 = p2.relativize(p1);

考虑一个稍微复杂的例子:

Path p1 = Paths.get("home");
Path p3 = Paths.get("home/sally/bar");
// Result is sally/bar
Path p1_to_p3 = p1.relativize(p3);
// Result is ../..
Path p3_to_p1 = p3.relativize(p1);

在此示例中,两个路径共享同一节点 home。要从 home 导航到 bar,首先将一个级别向下导航到 sally,然后再向下导航到 bar。从 bar 导航到 home 需要向上移动两个级别。

如果只有一个路径包含根元素,则不能构造相对路径。如果两个路径都包含根元素,则构造相对路径的能力取决于系统。

递归 Copy 示例使用 relativizeresolve 方法。

比较两条路径

Path 类支持 equals,使你可以测试两条路径是否相等。startsWithendsWith 方法使你能够测试路径是以特定字符串开头还是结尾。这些方法易于使用。例如:

Path path = ...;
Path otherPath = ...;
Path beginning = Paths.get("/home");
Path ending = Paths.get("foo");

if (path.equals(otherPath)) {
    // equality logic here
} else if (path.startsWith(beginning)) {
    // path begins with "/home"
} else if (path.endsWith(ending)) {
    // path ends with "foo"
}

Path 类实现 Iterable 接口。iterator 方法返回一个对象,使你可以迭代路径中的名称元素。返回的第一个元素是最接近目录树中的根的元素。以下代码片段遍历路径,打印每个名称元素:

Path path = ...;
for (Path name: path) {
    System.out.println(name);
}

Path 类还实现 Comparable 接口。你可以使用 compareTo 比较 Path 对象,这对排序很有用。

你还可以将 Path 对象放入 Collection。有关此强大功能的详细信息,请参阅 Collections 路径。

如果要验证两个 Path 对象是否定位同一文件,可以使用 isSameFile 方法,如 Checking Whether Two Paths Locate the Same File


Previous page: The Path Class
Next page: File Operations