• Java NIO之Path、Files


    参考文章(翻译): http://ifeve.com/java-nio-all/

    原文(英文)链接:https://jenkov.com/tutorials/java-nio/files.html

    Path

    背景

    Java Path接口是在Java7中添加到Java NIO的。Path接口位于java.nio.file包中,所以Path接口的完全限定名称为java.nio.file.Path

    Java Path实例表示文件系统中的路径。一个路径可以指向一个文件或一个目录。路径可以是绝对路径,也可以是相对路径。绝对路径包含从文件系统的根目录到它指向的文件或目录的完整路径。相对路径包含相对于其他路径的文件或目录的路径。

    创建

    为了使用java.nio.file.Path实例必须创建一个Path实例。您可以使用Paths类(java.nio.file.Paths)中的静态方法来创建路径实例,名为Paths.get()。下面是一个Java Paths.get()示例:

    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class PathExample {
    
        public static void main(String[] args) {
    		//可以使用Paths 类的静态方法Paths.get()来产生一个实例
            Path path = Paths.get("c:\\data\\myfile.txt");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    **注意:**可以配置相对路径或绝对路径。上述实例是传入绝对路径。

    绝对路径

    //windows中:示例中的绝对路径是c:\data\myfile.txt。有两个\字符的原因是第一个\是转义字符,表示紧跟着它的字符需要被转义。
    Path path = Paths.get("c:\\data\\myfile.txt");
    //在Unix系统(Linux,MacOS,FreeBSD等)中,上文中的path是这样的:
    Path path = Paths.get("/home/jakobjenkov/myfile.txt");
    
    • 1
    • 2
    • 3
    • 4

    相对路径

    //创建了一个指向d:\data\projects文件夹的实例
    Path projects = Paths.get("d:\\data", "projects");
    //创建了一个指向 d:\data\projects\a-project\myfile.txt 文件的实例
    Path file = Paths.get("d:\\data", "projects\\a-project\\myfile.txt");
    //d:\data\projects\another-project
    //在a-project目录后面的..符号,将指向的目录修改为projects目录,因此,最终path指向another-project目录。
    String path = "d:\\data\\projects\\a-project\\..\\another-project";
    Path parentDir2 = Paths.get(path);
    
    //同上
    Path path1 = Paths.get("d:\\data\\projects", ".\\a-project");
    Path path2 = Paths.get("d:\\data\\projects\\a-project",
     "..\\another-project");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    normalize

    Path 的normalize()方法可以标准化路径。标准化的含义是路径中的.和…都被去掉,指向真正的路径目录地址。

    String originalPath =
     "d:\\data\\projects\\a-project\\..\\another-project";
    
    Path path1 = Paths.get(originalPath);
    System.out.println("path1 = " + path1);
    
    Path path2 = path1.normalize();
    System.out.println("path2 = " + path2);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    输出:
    path1 = d:\data\projects\a-project\..\another-project
    path2 = d:\data\projects\another-project
    
    • 1
    • 2
    • 3

    Files

    java NIO Files类(java.nio.file.Files) 提供了操作文件的相关方法 。

    exists

    Files.exists()方法用来检查文件系统中是否存在某路径。

    Path实例对应的路径可能在文件系统中并不存在。例如,如果打算新建一个文件夹,首先需要创建一个对应的Path实例,然后才能创建对应路径下的文件夹。

    Path path = Paths.get("data/logging.properties");
    
    boolean pathExists =
     Files.exists(path,
     new LinkOption[]{ LinkOption.NOFOLLOW_LINKS});
    
    • 1
    • 2
    • 3
    • 4
    • 5

    示例中首先创建了一个Path。然后,通过调用Files.exists方法并将path作为第一个参数确认path对应的路径是否存在。
    注意下Files.exist()方法的第二个参数。第二个参数数组是评判路径是否存在时使用的规则。示例中,数组包含LinkOption.NOFOLLOW_LINKS枚举类型,表示Files.exists不会跟进到路径中有连接的下层文件目录。表示path路径中如果有连接,Files.exists方法不会跟进到连接中去 .

    createDirectory

    基于Path实例创建目录,

    Path path = Paths.get("data/subdir");
    
    try {
        Path newDir = Files.createDirectory(path);
    } catch(FileAlreadyExistsException e){
        // the directory already exists.
    } catch (IOException e) {
        //something else went wrong
        e.printStackTrace();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    第一行创建表示要创建的目录的 Path 实例。在 try-catch 块中,使用路径作为参数调用 Files.createDirectory() 方法。如果创建目录成功,则返回一个 Path 实例,该实例指向新创建的路径。

    如果目录已经存在,则会抛出 java.nio.file.FileAlreadyExistsException。如果出现其他问题,可能会抛出 IOException。例如,如果所需的新目录的父目录不存在,则可能会抛出 IOException。父目录是您要在其中创建新目录的目录。因此,它表示新目录的父目录。

    copy

    Files.copy() 方法将文件从一个路径复制到另一个路径。

    Path sourcePath      = Paths.get("data/logging.properties");
    Path destinationPath = Paths.get("data/logging-copy.properties");
    
    try {
        Files.copy(sourcePath, destinationPath);
    } catch(FileAlreadyExistsException e) {
        //destination file already exists
    } catch (IOException e) {
        //something else went wrong
        e.printStackTrace();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    首先,该示例创建一个源目标路径实例。然后该示例调用 Files.copy(),将两个 Path 实例作为参数传递。这将导致源路径引用的文件被复制到目标路径引用的文件中。

    如果目标文件已经存在,则抛出 java.nio.file.FileAlreadyExistsException。如果出现其他问题,将抛出 IOException。例如,如果要将文件复制到的目录不存在,则会抛出 IOException 。

    覆盖( 可以强制 Files.copy() 覆盖现有文件。下面是一个示例,展示了如何使用 Files.copy() 覆盖现有文件 ): 注意 Files.copy() 方法的第三个参数。如果目标文件已存在,此参数指示 copy() 方法覆盖现有文件。

    Path sourcePath      = Paths.get("data/logging.properties");
    Path destinationPath = Paths.get("data/logging-copy.properties");
    
    try {
        Files.copy(sourcePath, destinationPath,
                StandardCopyOption.REPLACE_EXISTING);
    } catch(FileAlreadyExistsException e) {
        //destination file already exists
    } catch (IOException e) {
        //something else went wrong
        e.printStackTrace();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    move

    Java NIO Files 类还包含一个用于将文件从一个路径移动到另一个路径的函数。移动的同时也可以重命名。

    Path sourcePath      = Paths.get("data/logging-copy.properties");
    Path destinationPath = Paths.get("data/subdir/logging-moved.properties");
    
    try {
        Files.move(sourcePath, destinationPath,
                StandardCopyOption.REPLACE_EXISTING);
    } catch (IOException e) {
        //moving file failed.
        e.printStackTrace();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    首先创建源路径和目标路径。源路径指向要移动的文件,目标路径指向文件应该移动到的位置。然后调用 Files.move() 方法。这导致文件被移动。

    注意传递给 Files.move() 的第三个参数。此参数告诉 Files.move() 方法覆盖目标路径中的任何现有文件。这个参数实际上是可选的。

    如果移动文件失败,Files.move() 方法可能会抛出 IOException。例如,如果目标路径中已经存在文件,并且您省略了 StandardCopyOption.REPLACE_EXISTING 选项,或者要移动的文件不存在等。

    delete

    Files.delete() 方法可以删除文件或目录

    Path path = Paths.get("data/subdir/logging-moved.properties");
    
    try {
        Files.delete(path);
    } catch (IOException e) {
        //deleting file failed
        e.printStackTrace();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    首先创建指向要删除的文件的路径。其次调用 Files.delete() 方法。如果 Files.delete() 由于某种原因无法删除文件(例如文件或目录不存在),则会引发 IOException。

    walkFileTree

    Files.walkFileTree() 方法包含递归遍历目录树的功能。

    static Path walkFileTree(Path start, Set<FileVisitOption> options, int maxDepth, FileVisitor<? super Path> visitor) throws IOException;
    static Path walkFileTree(Path start, FileVisitor<? super Path> visitor) throws IOException; FileVisitor 在遍历期间被调用。
    
    • 1
    • 2

    参数列表:
    java.nio.file.Path start 遍历的起始路径
    Set options 遍历选项
    int maxDepth 遍历深度
    java.nio.file.FileVisitor visitor 遍历过程中的行为控制器
    默认是深度优先遍历。

    FileVisitor

    public interface FileVisitor {
    
        public FileVisitResult preVisitDirectory(
            Path dir, BasicFileAttributes attrs) throws IOException;
    
        public FileVisitResult visitFile(
            Path file, BasicFileAttributes attrs) throws IOException;
    
        public FileVisitResult visitFileFailed(
            Path file, IOException exc) throws IOException;
    
        public FileVisitResult postVisitDirectory(
            Path dir, IOException exc) throws IOException {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    您必须自己实现 FileVisitor 接口,并将您的实现实例传递给 walkFileTree() 方法。 FileVisitor 实现的每个方法都将在目录遍历期间的不同时间被调用。如果您不需要挂钩所有这些方法,您可以扩展 SimpleFileVisitor 类,该类包含 FileVisitor 接口中所有方法的默认实现。

    返回值

    FileVisitor 实现中的每个方法都会在遍历期间的不同时间被调用:

    • 在访问任何目录之前调用 preVisitDirectory() 方法。

    • postVisitDirectory() 方法在访问目录后被调用。

    • 在文件遍历期间访问的每个文件都会调用 visitFile() 方法。不为目录调用它 - 只有文件。

    • 在访问文件失败的情况下调用 visitFileFailed() 方法。例如,如果您没有正确的权限,或者出现其他问题。

    这四个方法中的每一个都返回一个 FileVisitResult 枚举实例。 FileVisitResult 枚举包含以下四个选项:

    • CONTINUE
    • TERMINATE
    • SKIP_SIBLINGS
    • SKIP_SUBTREE

    通过返回这些值之一,被调用的方法可以决定文件遍历应该如何继续。

    CONTINUE继续意味着文件的执行应该像正常一样继续。

    TERMINATE终止意味着文件遍历现在应该终止。

    SKIP_SIBLINGS跳过同级意味着文件遍历应该继续,但不需要访问该文件或目录的任何同级。

    SKIP_SUBTREE跳过子级意味着文件遍历应该继续,但是不需要访问这个目录中的子目录。这个值只有从preVisitDirectory()返回时才是一个函数。如果从任何其他方法返回,它将被解释为一个CONTINUE继续。

    Files.walkFileTree(path, new FileVisitor<Path>() {
      @Override
      public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        System.out.println("pre visit dir:" + dir);
        return FileVisitResult.CONTINUE;
      }
    
      @Override
      public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        System.out.println("visit file: " + file);
        return FileVisitResult.CONTINUE;
      }
    
      @Override
      public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
        System.out.println("visit file failed: " + file);
        return FileVisitResult.CONTINUE;
      }
    
      @Override
      public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
        System.out.println("post visit directory: " + dir);
        return FileVisitResult.CONTINUE;
      }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    demo1

    这是一个 walkFileTree() 扩展 SimpleFileVisitor 以查找名为 README.txt 的文件:

    Path rootPath = Paths.get("data");
    String fileToFind = File.separator + "README.txt";
    
    try {
      Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
        
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
          String fileString = file.toAbsolutePath().toString();
          //System.out.println("pathString = " + fileString);
    
          if(fileString.endsWith(fileToFind)){
            System.out.println("file found at path: " + file.toAbsolutePath());
            return FileVisitResult.TERMINATE;
          }
          return FileVisitResult.CONTINUE;
        }
      });
    } catch(IOException e){
        e.printStackTrace();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    demo2

    Files.walkFileTree() 也可用于删除包含所有文件和子目录的目录。 Files.delete() 方法只会在目录为空时删除目录。通过遍历所有目录并删除每个目录中的所有文件(在 visitFile() 中),然后删除目录本身(在 postVisitDirectory() 中),您可以删除包含所有子目录和文件的目录。这是一个递归目录删除示例:

    Path rootPath = Paths.get("data/to-delete");
    
    try {
      Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
          System.out.println("delete file: " + file.toString());
          Files.delete(file);
          return FileVisitResult.CONTINUE;
        }
    
        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
          Files.delete(dir);
          System.out.println("delete dir: " + dir.toString());
          return FileVisitResult.CONTINUE;
        }
      });
    } catch(IOException e){
      e.printStackTrace();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    LeetCode --- 1422. Maximum Score After Splitting a String 解题报告
    文件上传漏洞
    训练营第三十五天动态规划(基础题part1)
    海外IP代理科普——API代理是什么?怎么用?
    Excel冻结窗格
    SpringBoot整合Redis(自动配置分析、切换Jedis、自定义序列化器)
    基础生态学名词解释
    计网补充问题 —— 摘自LeetBook
    [附源码]计算机毕业设计JAVAjsp社区新冠疫苗接种管理系统
    写给这批≥30岁的测试工程师
  • 原文地址:https://blog.csdn.net/weixin_43604021/article/details/126483330