• 文件操作NIO


    在丑陋的 Java I/O 编程方式诞生多年以后,Java终于简化了文件读写的基本操作。

    两个基本组件

    1. 文件或者目录的路径;
    2. 文件本身。

    这块基本都是些记忆性的东西,没什么过多的需要写的地方,用的时候搜一下就好,记住当然好,但是本人智商记不住。并且只看看NIO就可以。

    文件系统

    // files/FileSystemDemo.java
    import java.nio.file.*;
    
    public class FileSystemDemo {
        static void show(String id, Object o) {
            System.out.println(id + ": " + o);
        }
    
        public static void main(String[] args) {
            System.out.println(System.getProperty("os.name"));
            FileSystem fsys = FileSystems.getDefault();
            for(FileStore fs : fsys.getFileStores())
                show("File Store", fs);
            for(Path rd : fsys.getRootDirectories())
                show("Root Directory", rd);
            show("Separator", fsys.getSeparator());
            show("UserPrincipalLookupService",
                fsys.getUserPrincipalLookupService());
            show("isOpen", fsys.isOpen());
            show("isReadOnly", fsys.isReadOnly());
            show("FileSystemProvider", fsys.provider());
            show("File Attribute Views",
            fsys.supportedFileAttributeViews());
        }
    }
    /* 输出:
    Windows 10
    File Store: SSD (C:)
    Root Directory: C:\
    Root Directory: D:\
    Separator: \
    UserPrincipalLookupService:
    sun.nio.fs.WindowsFileSystem$LookupService$1@15db9742
    isOpen: true
    isReadOnly: false
    FileSystemProvider:
    sun.nio.fs.WindowsFileSystemProvider@6d06d69c
    File Attribute Views: [owner, dos, acl, basic, user]
    */

    路径监听

    通过 WatchService 可以设置一个进程对目录中的更改做出响应。在这个例子中,delTxtFiles() 作为一个单独的任务执行,该任务将遍历整个目录并删除以 .txt 结尾的所有文件,WatchService 会对文件删除操作做出反应:

    // files/PathWatcher.java
    // {ExcludeFromGradle}
    import java.io.IOException;
    import java.nio.file.*;
    import static java.nio.file.StandardWatchEventKinds.*;
    import java.util.concurrent.*;
    
    public class PathWatcher {
        static Path test = Paths.get("test");
    
        static void delTxtFiles() {
            try {
                Files.walk(test)
                .filter(f ->
                    f.toString()
                    .endsWith(".txt"))
                    .forEach(f -> {
                    try {
                        System.out.println("deleting " + f);
                        Files.delete(f);
                    } catch(IOException e) {
                        throw new RuntimeException(e);
                    }
                });
            } catch(IOException e) {
                throw new RuntimeException(e);
            }
        }
    
        public static void main(String[] args) throws Exception {
            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);
            }
        }
    }
    /* Output:
    deleting test\bag\foo\bar\baz\File.txt
    deleting test\bar\baz\bag\foo\File.txt
    deleting test\baz\bag\foo\bar\File.txt
    deleting test\foo\bar\baz\bag\File.txt
    deleting test\Hello.txt
    evt.context(): Hello.txt
    evt.count(): 1
    evt.kind(): ENTRY_DELETE
    */

    文件查找

    到目前为止,为了找到文件,我们一直使用相当粗糙的方法,在 path 上调用 toString(),然后使用 string 操作查看结果。事实证明,java.nio.file 有更好的解决方案:通过在 FileSystem 对象上调用 getPathMatcher() 获得一个 PathMatcher,然后传入您感兴趣的模式。模式有两个选项:glob 和 regexglob 比较简单,实际上功能非常强大,因此您可以使用 glob 解决许多问题。如果您的问题更复杂,可以使用 regex,这将在接下来的 Strings 一章中解释。

    在这里,我们使用 glob 查找以 .tmp 或 .txt 结尾的所有 Path

    // files/Find.java
    // {ExcludeFromGradle}
    import java.nio.file.*;
    
    public class Find {
        public static void main(String[] args) throws Exception {
            Path test = Paths.get("test");
            Directories.refreshTestDir();
            Directories.populateTestDir();
            // Creating a *directory*, not a file:
            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) // Only look for files
              .filter(Files::isRegularFile)
              .map(Path::getFileName)
              .filter(matcher2::matches)
              .forEach(System.out::println);
        }
    }
    /* Output:
    test\bag\foo\bar\baz\5208762845883213974.tmp
    test\bag\foo\bar\baz\File.txt
    test\bar\baz\bag\foo\7918367201207778677.tmp
    test\bar\baz\bag\foo\File.txt
    test\baz\bag\foo\bar\8016595521026696632.tmp
    test\baz\bag\foo\bar\File.txt
    test\dir.tmp
    test\foo\bar\baz\bag\5832319279813617280.tmp
    test\foo\bar\baz\bag\File.txt
    ***************
    5208762845883213974.tmp
    7918367201207778677.tmp
    8016595521026696632.tmp
    dir.tmp
    5832319279813617280.tmp
    ***************
    5208762845883213974.tmp
    7918367201207778677.tmp
    8016595521026696632.tmp
    5832319279813617280.tmp
    */

    在 matcher 中,glob 表达式开头的 **/ 表示“当前目录及所有子目录”,这在当你不仅仅要匹配当前目录下特定结尾的 Path 时非常有用。单 * 表示“任何东西”,然后是一个点,然后大括号表示一系列的可能性---我们正在寻找以 .tmp 或 .txt 结尾的东西。您可以在 getPathMatcher() 文档中找到更多详细信息。

    matcher2 只使用 *.tmp,通常不匹配任何内容,但是添加 map() 操作会将完整路径减少到末尾的名称。

    注意,在这两种情况下,输出中都会出现 dir.tmp,即使它是一个目录而不是一个文件。要只查找文件,必须像在最后 files.walk() 中那样对其进行筛选。

    文件读写

    文件很小的话

    // files/ListOfLines.java
    import java.util.*;
    import java.nio.file.*;
    
    public class ListOfLines {
        public static void main(String[] args) throws Exception {
            Files.readAllLines(
            Paths.get("../streams/Cheese.dat"))
            .stream()
            .filter(line -> !line.startsWith("//"))
            .map(line ->
                line.substring(0, line.length()/2))
            .forEach(System.out::println);
        }
    }
    /* Output:
    Not much of a cheese
    Finest in the
    And what leads you
    Well, it's
    It's certainly uncon
    */

    跳过注释行,其余的内容每行只打印一半。 这实现起来很简单:你只需将 Path 传递给 readAllLines() (以前的 java 实现这个功能很复杂)。readAllLines() 有一个重载版本,包含一个 Charset 参数来存储文件的 Unicode 编码。

    // files/Writing.java
    import java.util.*;
    import java.nio.file.*;
    
    public class Writing {
        static Random rand = new Random(47);
        static final int SIZE = 1000;
    
        public static void main(String[] args) throws Exception {
            // Write bytes to a file:
            byte[] bytes = new byte[SIZE];
            rand.nextBytes(bytes);
            Files.write(Paths.get("bytes.dat"), bytes);
            System.out.println("bytes.dat: " + Files.size(Paths.get("bytes.dat")));
    
            // Write an iterable to a file:
            List<String> lines = Files.readAllLines(
              Paths.get("../streams/Cheese.dat"));
            Files.write(Paths.get("Cheese.txt"), lines);
            System.out.println("Cheese.txt: " + Files.size(Paths.get("Cheese.txt")));
        }
    }
    /* Output:
    bytes.dat: 1000
    Cheese.txt: 199
    */

    如果文件大小有问题怎么办? 比如说:

    1. 文件太大,如果你一次性读完整个文件,你可能会耗尽内存。

    2. 您只需要在文件的中途工作以获得所需的结果,因此读取整个文件会浪费时间。

    Files.lines() 方便地将文件转换为行的 Stream

    流是很快的,且可控,这个例子就蛮不错的

    // files/StreamInAndOut.java
    import java.io.*;
    import java.nio.file.*;
    import java.util.stream.*;
    
    public class StreamInAndOut {
        public static void main(String[] args) {
            try(
              Stream<String> input =
                Files.lines(Paths.get("StreamInAndOut.java"));
              PrintWriter output =
                new PrintWriter("StreamInAndOut.txt")
            ) {
                input.map(String::toUpperCase)
                  .forEachOrdered(output::println);
            } catch(Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    切记,不要再用以前的java IO了,直接上手NIO,Files这个类很强

    一个没有高级趣味的人。 email:hushui502@gmail.com
  • 相关阅读:
    阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第2节 线程实现方式_5_主线程
    阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第2节 线程实现方式_4_线程调度
    阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第2节 线程实现方式_3_线程概念
    阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第2节 线程实现方式_2_进程概念
    阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第2节 线程实现方式_1_并发与并行
    阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第1节 异常_14_自定义异常类的练习
    阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第1节 异常_13_自定义异常类
    阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第1节 异常_12_异常注意事项_子父类异常
    阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第1节 异常_10_异常注意事项_多异常的捕获处理
    阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第1节 异常_9_finally代码块
  • 原文地址:https://www.cnblogs.com/CherryTab/p/11973704.html
Copyright © 2020-2023  润新知