Java I/O之文件系统

一、全文概览

在学习文件系统之前,需要了解下Java在I/O上的发展史:在Java7之前,打开和读取文件需要编写特别笨拙的代码,涉及到很多的InputStream、OutputStream等组合起来使用,每次在使用时或许都需要查一下文档才能记得如何打开一个文件;而在Java 7面世后,这些被人诟病的设计得到了巨大的改进,这些新元素被打包放在了java.nio.file之下。这个包对Java对文件的操作提升到了可以与其他编程语言媲美的程度。

本篇文章就主要学习记录下操作文件的两个基本组件:

  • 文件或目录的路径-Path
  • 文件本身-File

二、组件一:Path(路径)

1、Path简介

Path对象代表的是一个文件或目录的路径,它是在不同的操作系统和文件系统之上的抽象。它的目的是,我们不必要注意底层的操作系统,我们的代码不需要重写就能在不同的操作系统上工作 —来自:《OnJava-基础篇》

2、Path创建

介绍完Path出现的目的后,我们再来看下如果获取Path。正是java.nio.file.Paths类下重载的static get方法,这个方法的参数可以是接受一个String序列(也就是全路径,系统能够找到的);也可以是一个统一的资源标识符(URI),然后将其转换为Path

下面是对Path以及Files工具类的使用示例

package com.markus.java.file;

import com.markus.java.file.constant.FileConstant;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * @Author: zhangchenglong06
 * @Date: 2023/2/7
 * @Description:
 */
public class PathInfo {

    static void show(String id, Object p) {
        System.out.println(id + p);
    }

    static void info(Path path) {
        show("toString: \n", path);
        // 路径是否存在
        show("Exists: ", Files.exists(path));
        // 是否是常规文件,也就是我们常规意义上的文件,例如.txt .png等,而非目录
        show("RegularFile: ", Files.isRegularFile(path));
        // 是否是目录
        show("Directory: ", Files.isDirectory(path));
        // 展示绝对路径
        show("Absolute: ", path.toAbsolutePath());
        // 展示文件名(最后一级)
        show("FileName: ", path.getFileName());
        // 展示父路径
        show("Parent: ", path.getParent());
        // 展示根路径
        show("Root: ", path.getRoot());
        System.out.println("****************************************************************************************");
    }

    public static void main(String[] args) {
        System.out.println(System.getProperty("os.name"));
        info(FileSystems.getDefault().getPath(""));
        info(Paths.get("io/src/main/resources", "file", "file.txt"));
        Path path = Paths.get(FileConstant.CURRENT_BASE_PATH, "/PathInfo.java");
        info(path);
        Path ap = path.toAbsolutePath();
        info(ap);
        info(ap.getParent());
        try {
            info(ap.toRealPath());
        } catch (IOException e) {
            System.out.println(e);
        }
        URI uri = path.toUri();
        System.out.println("URI:\n" + uri);
        Path pathUri = Paths.get(uri);
        System.out.println(Files.exists(pathUri));
        // 不要被骗了
        File f = ap.toFile();
    }
}
/** 输出
 * Mac OS X
 * toString:
 *
 * Exists: true
 * RegularFile: false
 * Directory: true
 * Absolute: /Users/zhangchenglong/IdeaProjects/OnJava8-Examples
 * FileName:
 * Parent: null
 * Root: null
 * ****************************************************************************************
 * toString:
 * io/src/main/resources/file/file.txt
 * Exists: false
 * RegularFile: false
 * Directory: false
 * Absolute: /Users/zhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/resources/file/file.txt
 * FileName: file.txt
 * Parent: io/src/main/resources/file
 * Root: null
 * ****************************************************************************************
 * toString:
 * io/src/main/java/com/markus/java/file/PathInfo.java
 * Exists: true
 * RegularFile: true
 * Directory: false
 * Absolute: /Users/zhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java/file/PathInfo.java
 * FileName: PathInfo.java
 * Parent: io/src/main/java/com/markus/java/file
 * Root: null
 * ****************************************************************************************
 * toString:
 * /Users/zhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java/file/PathInfo.java
 * Exists: true
 * RegularFile: true
 * Directory: false
 * Absolute: /Users/zhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java/file/PathInfo.java
 * FileName: PathInfo.java
 * Parent: /Users/zhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java/file
 * Root: /
 * ****************************************************************************************
 * toString:
 * /Users/zhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java/file
 * Exists: true
 * RegularFile: false
 * Directory: true
 * Absolute: /Users/zhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java/file
 * FileName: file
 * Parent: /Users/zhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java
 * Root: /
 * ****************************************************************************************
 * toString:
 * /Users/zhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java/file/PathInfo.java
 * Exists: true
 * RegularFile: true
 * Directory: false
 * Absolute: /Users/zhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java/file/PathInfo.java
 * FileName: PathInfo.java
 * Parent: /Users/zhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java/file
 * Root: /
 * ****************************************************************************************
 * URI:
 * file:///Users/zhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java/file/PathInfo.java
 * true
 *
 * Process finished with exit code 0
 */

3、Path片段

所谓Path片段即是Path对象路径的各个部分,例如/Users/zhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java中每个//之间的内容都是一个片段

package com.markus.java.file;

import com.markus.java.file.constant.FileConstant;

import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * @Author: zhangchenglong06
 * @Date: 2023/2/7
 * @Description:
 */
public class PartsOfPaths {
    public static void main(String[] args) {
        System.out.println(System.getProperty("os.name"));
        Path p = Paths.get(FileConstant.CURRENT_BASE_PATH, "/PartsOfPaths.java");
        for (int i = 0; i < p.getNameCount(); i++) {
            System.out.println(p.getName(i));
        }
        System.out.println("ends with '.java': " + p.endsWith(".java"));
        for (Path path : p) {
            System.out.print(path + ": ");
            System.out.print(p.startsWith(path) + " : ");
            System.out.println(p.endsWith(path));
        }

        System.out.println("Starts with " + p.toAbsolutePath().getRoot() + " " + p.toAbsolutePath().startsWith(p.toAbsolutePath().getRoot()));
    }
}
/** 输出
 * Mac OS X
 * io
 * src
 * main
 * java
 * com
 * markus
 * java
 * file
 * PartsOfPaths.java
 * ends with '.java': false
 * io: true : false
 * src: false : false
 * main: false : false
 * java: false : false
 * com: false : false
 * markus: false : false
 * java: false : false
 * file: false : false
 * PartsOfPaths.java: false : true
 * Starts with / true
 */

我们还可以对片段进行添加和删除,需要学习api如下:

  • Path#relativize : 在路径首部删除基准路径形成新的路径
  • Path#normalize : 删除冗余名称元素的路径
  • Path#resolve : 在Path对象后面增加路径片段
package com.markus.java.file;

import com.markus.java.file.constant.FileConstant;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * @Author: zhangchenglong06
 * @Date: 2023/2/8
 * @Description: 添加或删除路径片段
 * relativize 删除基准路径
 * resolve 增加路径
 */
public class AddAndSubtractPaths {
    static Path base = Paths.get("..", "..", "..").toAbsolutePath().normalize();

    static void show(int id, Path result) {
        if (result.isAbsolute()) {
            // result 在 路径首部删除base基准路径形成新的路径
            System.out.println("(" + id + ")r" + base.relativize(result));
        } else {
            System.out.println("(" + id + ")" + result);
        }
        try {
            System.out.println("RealPath: " + result.toRealPath());
        } catch (IOException e) {
            System.out.println(e);
        }
    }

    public static void main(String[] args) {
        System.out.println(System.getProperty("os.name"));
        System.out.println(base);
        Path p = Paths.get(FileConstant.CURRENT_BASE_PATH, "/AddAndSubtractPaths.java").toAbsolutePath();
        show(1, p);
        Path convoluted = p.getParent().getParent().resolve("strings").resolve("..").resolve(p.getParent().getFileName());
        show(2, convoluted);
        show(3, convoluted.normalize());

        Path p2 = Paths.get("..", "..");
        show(4, p2);
        // normalize 意为删除了冗余名称元素的路径
        // 例如 ./ 直接删除即可
        // 例如 strings/../ 直接删除两个元素即可
        show(5, p2.normalize());
        show(6, p2.toAbsolutePath());

        Path p3 = Paths.get(".").toAbsolutePath();
        // p3在自身路径后面追加p2组成新的路径
        Path p4 = p3.resolve(p2);
        show(7, p4);
        show(8, p4.normalize());

        Path p5 = Paths.get("").toAbsolutePath();
        show(9, p5);
        show(10, p5.resolveSibling("strings"));
        show(11, Paths.get("nonexistent"));
    }
}
/** 输出
 *  Mac OS X
 * /Users
 * (1)rzhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java/file/AddAndSubtractPaths.java
 * RealPath: /Users/zhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java/file/AddAndSubtractPaths.java
 * (2)rzhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java/strings/../file
 * java.nio.file.NoSuchFileException: /Users/zhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java/strings/../file
 * (3)rzhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java/file
 * RealPath: /Users/zhangchenglong/IdeaProjects/OnJava8-Examples/io/src/main/java/com/markus/java/file
 * (4)../..
 * RealPath: /Users/zhangchenglong
 * (5)../..
 * RealPath: /Users/zhangchenglong
 * (6)rzhangchenglong/IdeaProjects/OnJava8-Examples/../..
 * RealPath: /Users/zhangchenglong
 * (7)rzhangchenglong/IdeaProjects/OnJava8-Examples/./../..
 * RealPath: /Users/zhangchenglong
 * (8)rzhangchenglong
 * RealPath: /Users/zhangchenglong
 * (9)rzhangchenglong/IdeaProjects/OnJava8-Examples
 * RealPath: /Users/zhangchenglong/IdeaProjects/OnJava8-Examples
 * (10)rzhangchenglong/IdeaProjects/strings
 * java.nio.file.NoSuchFileException: /Users/zhangchenglong/IdeaProjects/strings
 * (11)nonexistent
 * java.nio.file.NoSuchFileException: nonexistent
 */

4、删除目录

Files工具类包含了操作目录和文件所需要的大部分操作,但其中缺乏删除目录树的工具方法,这里我们做个删除目录树工具的练习

package com.markus.java.file;

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

/**
 * @Author: zhangchenglong06
 * @Date: 2023/2/9
 * @Description:
 */
public class RmDir {
  public static void rmdir(Path dir) throws IOException {
    Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
      /**
       * 在这个目录下的每个文件上运行
       * @param file
       * @param attrs
       * @return
       * @throws IOException
       */
      @Override
      public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        Files.delete(file);
        return FileVisitResult.CONTINUE;
      }

      /**
       * 先进入当前目录下的文件和目录(包括所有的子目录),最后在当前目录上运行
       * @param dir
       * @param exc
       * @return
       * @throws IOException
       */
      @Override
      public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
        Files.delete(dir);
        return FileVisitResult.CONTINUE;
      }


      /**
       * 先在当前目录上运行,然后进入这个目录下的文件和目录
       * @param dir
       * @param attrs
       * @return
       * @throws IOException
       */
      @Override
      public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        return super.preVisitDirectory(dir, attrs);
      }

      /**
       * 当文件无法访问时调用
       * @param file
       * @param exc
       * @return
       * @throws IOException
       */
      @Override
      public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
        return super.visitFileFailed(file, exc);
      }
    });
  }
}

下面来检验下删除工具的有效性

package com.markus.java.file;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static com.markus.java.file.constant.FileConstant.CURRENT_BASE_PATH;


/**
 * @Author: zhangchenglong06
 * @Date: 2023/2/9
 * @Description:
 */
public class Directories {
  static Path test = Paths.get(CURRENT_BASE_PATH, "test");
  static String sep = FileSystems.getDefault().getSeparator();
  static List<String> parts = Arrays.asList("foo", "bar", "baz", "bag");

  static Path makeVariant() {
    // 移动指定列表的元素 distance 位
    // 例如 [1, 2, 3, 4, 5] 执行Collections.rotate(list.subList(1,4),1)
    // 结果 [1, 4, 3, 4, 5]
    Collections.rotate(parts, 1);
    return Paths.get(CURRENT_BASE_PATH, "test", String.join(sep, parts));
  }

  static void refreshTestDir() throws IOException {
    // 如果文件存在则删除,否则创建目录
    if (Files.exists(test)) {
      RmDir.rmdir(test);
    }
    if (!Files.exists(test)) {
      Files.createDirectory(test);
    }
  }

  static void populateTestDir() throws IOException {
    for (int i = 0; i < parts.size(); i++) {
      Path variant = makeVariant();
      if (!Files.exists(variant)) {
        Files.createDirectories(variant);
        Files.copy(Paths.get(CURRENT_BASE_PATH, "Directories.java"), variant.resolve("File" + i + ".txt"));
        Files.createTempFile(variant, null, null);
      }
    }
  }

  public static void main(String[] args) throws IOException {
    // 刷新测试目录
    refreshTestDir();
    // 在test路径下增加Hello.txt文件路径 并创建此路径文件
    Files.createFile(test.resolve("Hello.txt"));
    // 创建文件
    Path variant = makeVariant();
    try {
      // 创建一级目录的函数,当创建多级目录时会抛出文件不存在的异常
      Files.createDirectory(variant);
    } catch (Exception e) {
      System.out.println("Nope, that doesn't work." + e);
    }
    populateTestDir();
    Path tempDir = Files.createTempDirectory(test, "DIR_");
    Files.createTempFile(tempDir, "pre", ".non");
    // newDirectoryStream 只显示指定目录下的子目录或文件
    Files.newDirectoryStream(test).forEach(System.out::println);
    System.out.println("********************");
    // walk 可以浏览指定目录下的所有内容(目录树)
    Files.walk(test).forEach(System.out::println);
  }
}
/** 控制台
 * Nope, that doesn't work.java.nio.file.NoSuchFileException: io/src/main/java/com/markus/java/file/test/bag/foo/bar/baz
 * io/src/main/java/com/markus/java/file/test/DIR_260744175083605522
 * io/src/main/java/com/markus/java/file/test/foo
 * io/src/main/java/com/markus/java/file/test/baz
 * io/src/main/java/com/markus/java/file/test/bar
 * io/src/main/java/com/markus/java/file/test/bag
 * io/src/main/java/com/markus/java/file/test/Hello.txt
 * ********************
 * io/src/main/java/com/markus/java/file/test
 * io/src/main/java/com/markus/java/file/test/DIR_260744175083605522
 * io/src/main/java/com/markus/java/file/test/DIR_260744175083605522/pre7111318949801779914.non
 * io/src/main/java/com/markus/java/file/test/foo
 * io/src/main/java/com/markus/java/file/test/foo/bar
 * io/src/main/java/com/markus/java/file/test/foo/bar/baz
 * io/src/main/java/com/markus/java/file/test/foo/bar/baz/bag
 * io/src/main/java/com/markus/java/file/test/foo/bar/baz/bag/File2.txt
 * io/src/main/java/com/markus/java/file/test/foo/bar/baz/bag/2694156263661518806.tmp
 * io/src/main/java/com/markus/java/file/test/baz
 * io/src/main/java/com/markus/java/file/test/baz/bag
 * io/src/main/java/com/markus/java/file/test/baz/bag/foo
 * io/src/main/java/com/markus/java/file/test/baz/bag/foo/bar
 * io/src/main/java/com/markus/java/file/test/baz/bag/foo/bar/File0.txt
 * io/src/main/java/com/markus/java/file/test/baz/bag/foo/bar/6305328343447758907.tmp
 * io/src/main/java/com/markus/java/file/test/bar
 * io/src/main/java/com/markus/java/file/test/bar/baz
 * io/src/main/java/com/markus/java/file/test/bar/baz/bag
 * io/src/main/java/com/markus/java/file/test/bar/baz/bag/foo
 * io/src/main/java/com/markus/java/file/test/bar/baz/bag/foo/File1.txt
 * io/src/main/java/com/markus/java/file/test/bar/baz/bag/foo/5478658539929705751.tmp
 * io/src/main/java/com/markus/java/file/test/bag
 * io/src/main/java/com/markus/java/file/test/bag/foo
 * io/src/main/java/com/markus/java/file/test/bag/foo/bar
 * io/src/main/java/com/markus/java/file/test/bag/foo/bar/baz
 * io/src/main/java/com/markus/java/file/test/bag/foo/bar/baz/File3.txt
 * io/src/main/java/com/markus/java/file/test/bag/foo/bar/baz/5014150493260271159.tmp
 * io/src/main/java/com/markus/java/file/test/Hello.txt
 */

5、Path监听和查找

FileSystems(文件系统)可以获得两个的API:

  • WatchService: 监听Path
  • TreeMatcher: 查找Path

下面我们看下代码示例:

  • WatchService
package com.markus.java.file;


import com.markus.java.file.constant.FileConstant;

import java.io.IOException;
import java.nio.file.*;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;

/**
 * @Author: zhangchenglong06
 * @Date: 2023/2/10
 * @Description:
 */
public class PathWatcher {
  static Path test = Paths.get(FileConstant.CURRENT_BASE_PATH, "test");

  static void delTxtFiles() {
    try {
      Files.walk(test)
          .filter(
              f -> f.toString().endsWith(".txt")
          ).forEach(
          f -> {
            System.out.println("deleting " + f);
            try {
              Files.delete(f);
            } catch (IOException e) {
              throw new RuntimeException(e);
            }
          });
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  public static void main(String[] args) throws IOException, InterruptedException {
    // 刷新目录
    Directories.refreshTestDir();
    // 创建目录下的文件
    Directories.populateTestDir();

    Files.createFile(test.resolve("Hello.txt"));
    WatchService watcher = FileSystems.getDefault().newWatchService();
    // 只能监听到当前目录下操作,子目录下的文件操作不会被监听到
    test.register(watcher, ENTRY_DELETE);
    Executors.newSingleThreadScheduledExecutor().schedule(PathWatcher::delTxtFiles, 250, TimeUnit.MILLISECONDS);

    WatchKey key = watcher.take();
    for (WatchEvent<?> evt : key.pollEvents()) {
      System.out.println("evt.context(): " + evt.context() +
          "\nevt.count(): " + evt.count() +
          "\nevt.kind(): " + evt.kind());
      System.exit(0);
    }

  }
}
/** 控制台
 * deleting io/src/main/java/com/markus/java/file/test/foo/bar/baz/bag/File3.txt
 * deleting io/src/main/java/com/markus/java/file/test/baz/bag/foo/bar/File1.txt
 * deleting io/src/main/java/com/markus/java/file/test/bar/baz/bag/foo/File2.txt
 * deleting io/src/main/java/com/markus/java/file/test/bag/foo/bar/baz/File0.txt
 * deleting io/src/main/java/com/markus/java/file/test/Hello.txt
 * evt.context(): Hello.txt
 * evt.count(): 1
 * evt.kind(): ENTRY_DELETE
 */

上面的代码会有个缺陷,就是它只能监听到当前目录的操作,而无法查找当前目录下所有的子目录的操作行为,如果想监听整个目录树,则必须在整个树的每个子目录上设置一个WatchService

package com.markus.java.file;

import com.markus.java.file.constant.FileConstant;

import java.io.IOException;
import java.nio.file.*;
import java.util.concurrent.Executors;

import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;

/**
 * @Author: zhangchenglong06
 * @Date: 2023/2/10
 * @Description:
 */
public class TreeWatcher {
    static void watchDir(Path dir) {
        try {
            WatchService watcher = FileSystems.getDefault().newWatchService();
            dir.register(watcher, ENTRY_DELETE);
            Executors.newSingleThreadScheduledExecutor().submit(() -> {
                try {
                    WatchKey key = watcher.take();
                    for (WatchEvent<?> evt : key.pollEvents()) {
                        System.out.println("evt.context(): " + evt.context() +
                                "\nevt.count(): " + evt.count() +
                                "\nevt.kind(): " + evt.kind());
                        System.exit(0);
                    }
                } catch (InterruptedException e) {
                    return;
                }
            });
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws IOException {
        Directories.refreshTestDir();
        Directories.populateTestDir();
        Files.walk(Paths.get(FileConstant.CURRENT_BASE_PATH, "test"))
                .filter(Files::isDirectory)
                .forEach(TreeWatcher::watchDir);
        PathWatcher.delTxtFiles();
    }
}
/** 控制台
 * deleting io/src/main/java/com/markus/java/file/test/foo/bar/baz/bag/File3.txt
 * deleting io/src/main/java/com/markus/java/file/test/baz/bag/foo/bar/File1.txt
 * deleting io/src/main/java/com/markus/java/file/test/bar/baz/bag/foo/File2.txt
 * deleting io/src/main/java/com/markus/java/file/test/bag/foo/bar/baz/File0.txt
 * evt.context(): File1.txt
 * evt.count(): 1
 * evt.kind(): ENTRY_DELETE
 * evt.context(): File0.txt
 * evt.count(): 1
 * evt.kind(): ENTRY_DELETE
 */
  • PathMatcher
package com.markus.java.file;


import com.markus.java.file.constant.FileConstant;

import java.io.IOException;
import java.nio.file.*;

/**
 * @Author: zhangchenglong06
 * @Date: 2023/2/10
 * @Description:
 */
public class Find {
  public static void main(String[] args) throws IOException {
    Path test = Paths.get(FileConstant.CURRENT_BASE_PATH, "test");
    Directories.refreshTestDir();
    Directories.populateTestDir();
    Files.createDirectory(test.resolve("dir.tmp"));

    PathMatcher matcher = FileSystems.getDefault()
        .getPathMatcher("glob:**/*.{tmp,txt}");
    Files.walk(test)
        .filter(matcher::matches)
        .forEach(System.out::println);
    System.out.println("*******************");

    PathMatcher matcher2 = FileSystems.getDefault()
        .getPathMatcher("glob:*.tmp");
    Files.walk(test)
        .map(Path::getFileName)
        .filter(matcher2::matches)
        .forEach(System.out::println);
    System.out.println("*******************");

    Files.walk(test)
        .filter(Files::isRegularFile)
        .map(Path::getFileName)
        .filter(matcher2::matches)
        .forEach(System.out::println);
  }
}
/** 控制台
 * io/src/main/java/com/markus/java/file/test/foo/bar/baz/bag/File3.txt
 * io/src/main/java/com/markus/java/file/test/foo/bar/baz/bag/2767500121528410890.tmp
 * io/src/main/java/com/markus/java/file/test/baz/bag/foo/bar/File1.txt
 * io/src/main/java/com/markus/java/file/test/baz/bag/foo/bar/5789074899285883862.tmp
 * io/src/main/java/com/markus/java/file/test/dir.tmp
 * io/src/main/java/com/markus/java/file/test/bar/baz/bag/foo/File2.txt
 * io/src/main/java/com/markus/java/file/test/bar/baz/bag/foo/5458649986440731775.tmp
 * io/src/main/java/com/markus/java/file/test/bag/foo/bar/baz/File0.txt
 * io/src/main/java/com/markus/java/file/test/bag/foo/bar/baz/5568292198534480479.tmp
 * *******************
 * 2767500121528410890.tmp
 * 5789074899285883862.tmp
 * dir.tmp
 * 5458649986440731775.tmp
 * 5568292198534480479.tmp
 * *******************
 * 2767500121528410890.tmp
 * 5789074899285883862.tmp
 * 5458649986440731775.tmp
 * 5568292198534480479.tmp
 */

三、组件二:File(文件)

在上面,主要学习记录了对路径目录的操作,现在来学习下如果操作文件本身的内容,也就是如何进行读写文件:

  • 通过Files.readAllLines 可以一次性的读入整个文件,生成一个List。ps:这里不建议一次性读入大文件,会撑爆内存的
package com.markus.java.file;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * @author: markus
 * @date: 2023/2/15 10:39 PM
 * @Description:
 * @Blog: https://markuszhang.com
 * It's my honor to share what I've learned with you!
 */
public class ListOfLines {
    public static void main(String[] args) throws IOException {
        Files.readAllLines(Paths.get("io/src/main/resources/file_writer.txt"))
                .forEach(System.out::println);
    }
}
/** 控制台
 * Hello,IO
 */
  • 通过Files.write可以将byte数组或任何实现了Iterable接口的类的对象写入文件
package com.markus.java.file;

import com.markus.java.file.constant.FileConstant;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Random;

/**
 * @author: markus
 * @date: 2023/2/16 10:33 PM
 * @Description:
 * @Blog: https://markuszhang.com
 * It's my honor to share what I've learned with you!
 */
public class Writing {
    static Random rand = new Random(47);

    static final int SIZE = 1000;

    public static void main(String[] args) throws IOException {
        // 将字节写入一个文件
        byte[] bytes = new byte[SIZE];
        rand.nextBytes(bytes);
//        Files.write(Paths.get(FileConstant.CURRENT_BASE_PATH, "bytes.txt"), bytes, Charset.forName("charset"));
        Files.write(Paths.get(FileConstant.CURRENT_BASE_PATH, "bytes.txt"), bytes);
        System.out.println("bytes.txt: " + Files.size(Paths.get(FileConstant.CURRENT_BASE_PATH, "bytes.txt")));

        // 将实现了Iterable接口的类的对象写入一个文件
        List<String> lines = Files.readAllLines(Paths.get("io/src/main/resources/file_writer.txt"));
        Files.write(Paths.get("io/src/main/resources/file_writer.txt"), lines);
        System.out.println("file_writer.txt: " + Files.size(Paths.get("io/src/main/resources/file_writer.txt")));
    }
}
/**
 * 控制台
 * bytes.txt: 1000
 * file_writer.txt: 9
 */
  • 通过Files.lines可以很方便的将一个文件变为一个由行组成的Stream,这样可以应对当文件非常大,而我们只需要文件的一部分时,进行读取,而非全部读入内存当中
package com.markus.java.file;

import com.markus.java.file.constant.FileConstant;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * @author: markus
 * @date: 2023/2/16 10:47 PM
 * @Description:
 * @Blog: https://markuszhang.com
 * It's my honor to share what I've learned with you!
 */
public class ReadLineStream {
    public static void main(String[] args) throws IOException {
        Files.lines(Paths.get(FileConstant.CURRENT_BASE_PATH, "PathInfo.java"))
                .skip(13)
                .findFirst()
                .ifPresent(System.out::println);
    }
}
/** 控制台
 *  * @Author: zhangchenglong06
 */
  • 通过Files.lines我们也可以实现由行组成的输入流来在流中完成读取、处理、写入等操作
package com.markus.java.file;

import com.markus.java.file.constant.FileConstant;

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;

/**
 * @author: markus
 * @date: 2023/2/16 10:52 PM
 * @Description:
 * @Blog: https://markuszhang.com
 * It's my honor to share what I've learned with you!
 */
public class StreamInAndOut {
    public static void main(String[] args) {
        try (
                Stream<String> input = Files.lines(Paths.get(FileConstant.CURRENT_BASE_PATH, "StreamInAndOut.java"));
                PrintWriter writer = new PrintWriter(FileConstant.CURRENT_BASE_PATH + "StreamInAndOut.txt");
        ) {
            input.map(String::toUpperCase)
                    .forEachOrdered(writer::println);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

四、全文总结

本文记录了基于Path、Paths、File、Files等抽象对文件、目录相关的操作,摒弃了传统的Java I/O操作类,它使得我们对文件等的操作更加简单,它屏蔽了用户与底层的交互,也使得代码可以跨平台运行。

你可能感兴趣的:(java,开发语言)