使用java.nio.file 库优雅的操作文件详解-kb88凯时官网登录

来自:网络
时间:2023-05-17
阅读:
免费资源网 - https://freexyz.cn/
目录

概述

在早期的 java 版本中,文件 io 操作功能一直相对较弱,主要存在以下问题:

  • 缺乏对现代文件系统的支持:只提供的基础的文件操作,不支持很多现代的文件系统
  • api 不够直观:文件操作的 api 设计相对较为复杂和冗长,使用体验感很差
  • 对于大文件处理和并发性能不够:简单的 i/o 模型,没有充分利用现代硬件的性能优势,而且还有很多同步的问题

但 java 在后期版本中引入了 java.nio.file 库来提高 java 对文件操作的能力。还增加的流的功能,似乎使得文件变成更好用了。所以本章,我们就来主要介绍 java.nio.file 中常用的类和模块,大致如下:

  • path 路径:paths 模块和 path 工具类介绍
  • files 文件:file 和 filesystems 工具类介绍
  • 文件管理服务:watchservice 、pathmatcher 等等文件服务

path 路径

java.nio.file.paths 和 java.nio.file.path 类在 java nio 文件 i/o 框架中用于处理文件系统路径。以下是对它们的简单介绍:

  • paths 模块:paths 模块提供了一些静态方法来创建 path 对象,path 对象表示文件系统中的路径。例如,可以使用 paths.get() 方法创建一个 path 对象,这个对象表示一个文件路径。
  • path 类:path 类代表一个文件系统中的路径,它提供了一系列的方法来操作文件路径。例如,可以使用 path.toabsolutepath() 方法获取一个绝对路径,或者使用 path.getparent() 方法获取路径的父路径。

关于跨平台:path 对象可以工作在不同操作系统的不同文件系统之上,它帮我们屏蔽了操作系统之间的差异

以下是一些简单使用场景示例:

import java.nio.file.path;
import java.nio.file.paths;
public class pathexample {
    public static void main(string[] args) {
        // 创建一个绝对路径
        path absolutepath = paths.get("c:\\users\\phoenix\\file.txt");     // 这里传入 "example\\file.txt" 创建的相对路径
        system.out.println("absolute path: "   absolutepath);
        // 获取父路径
        system.out.println("parent path: "   absolutepath.getparent());
        // 获取文件名
        system.out.println("file name: "   absolutepath.getfilename());
        // 获取根路径
        system.out.println("root path: "   absolutepath.getroot());
        // 合并路径
        path resolvepath = paths.get("c:\\users\\phoenix").resolve("file.txt");
        system.out.println("merged path:"   resolvepath);
    }
}

输出结果:

absolute path: c:\users\phoenix\file.txt
parent path: c:\users\phoenix
file name: file.txt
root path: c:\
merged path:c:\users\phoenix\file.txt

从这里你不仅可以看出关于 paths 和 path 类对于文件路径的一些操作方法的使用,还能看得出我使用的是 windows 操作系统。还有更多的用法可以查看官方的 api 文档,这里就不过多赘述了。

files 文件

java.nio.file.files 类是 java nio 文件包中的一个实用工具类,它提供了一系列静态方法,可以让你方便地执行文件系统中的各种操作,例如文件的创建、删除、复制、移动、读取和写入等。例如,可以使用 files.exists() 方法检查一个文件是否存在,或者使用 files.createdirectory() 方法创建一个新目录。

以下是一些简单使用场景示例:

import java.io.ioexception;
import java.nio.charset.standardcharsets;
import java.nio.file.*;
import java.util.arrays;
import java.util.list;
public class pathexample {
    public static void main(string[] args) throws ioexception {
        path path = paths.get("example.txt");
        // 1:检查文件是否存在
        boolean exists = files.exists(path);
        system.out.println("file exists: "   exists);
        if (!exists) {
            // 2:不存在则创建文件
            files.createfile(path);
        }
        // 3:复制一个文件
        path target = paths.get("example2.txt");
        files.copy(path, target, standardcopyoption.replace_existing);
        // 4:创建目录
        path newdirectory = paths.get("example");
        files.createdirectories(newdirectory);
        // 4:移动文件:将 example2.txt 移动到 example 目录下
        files.move(target, newdirectory.resolve("example2.txt"), standardcopyoption.replace_existing);
        // 5:删除文件和目录
        files.delete(newdirectory.resolve("example2.txt"));
        files.delete(newdirectory);				// 只能删除空目录
        // 6:将字节数组写入文件
        files.write(path, "hello world".getbytes());
        // 7:将文本行序列写入文件
        list lines = arrays.aslist("line 1", "line 2", "line 3");
        files.write(path, lines, standardcharsets.utf_8, standardopenoption.create);
        // 8:读取文件,并且打印所有行
        files.readalllines(path, standardcharsets.utf_8).foreach(system.out::println);
    }
}

输出结果:

file exists: true
line 1
line 2
line 3

也可以在项目根目录下查看文件:

以上代码示例展示了如何使用 files 类进行常见的文件操作。在实际项目中,您可以根据需要组合使用这些方法来满足您的需求。

补充:

files.delete 函数只能删除空目录,这个设计是有意为之的,因为递归地删除文件和目录可能是一个非常危险的操作,尤其是当您不小心删除了一个包含重要数据的目录时。如果您想删除一个包含子目录和文件的目录,您需要先递归地删除目录中的所有子目录和文件,然后再删除目录本身。可以借助 files.walkfiletree 遍历文件目录,然后调用 files.delete 即可。

filesystems 文件系统

filesystems 类提供了一组静态方法来访问和操作默认文件系统(通常是操作系统的本地文件系统)以及其他文件系统实现。以下是一个简单的示例:

public class filesystemsexample {
    public static void main(string[] args) {
        // 获取默认文件系统
        filesystem filesystem = filesystems.getdefault();
        // 获取文件系统的路径分隔符
        string pathseparator = filesystem.getseparator();
        system.out.println("path separator: "   pathseparator);
        // 获取文件系统的根目录
        for (path root : filesystem.getrootdirectories()) {
            system.out.println("root directory: "   root);
        }
        // 使用文件系统创建一个 path 路径对象
        path path = filesystem.getpath("path", "to", "file.txt");
        system.out.println(path);
        // 是否只读
        system.out.println("is read only ?: "   filesystem.isreadonly());
        // 文件系统的提供者
        system.out.println("provider: "   filesystem.provider());
    }
}

输出结果:

path separator: \
root directory: c:\
path\to\file.txt
is read only ?: false
provider: sun.nio.fs.windowsfilesystemprovider@5b480cf9

filesystem 工具类的方法并不多,可以参考它的 api,但通过 filesystem 可以创建 watchservice 和 pathmatcher 子类

watchservice 文件监控

watchservice 是一个文件系统观察者,基于 filesystem 创建,主要用于监控文件系统事件(如创建、修改、删除文件或目录)。它可以帮助我们实时地检测和处理文件系统中的变化。如果你的业务中有需要监控文件变化的场景,你可能会需要用到它,例如:

  • 文件上传
  • 实时备份
  • 热加载配置

以下是一个简单的示例:

import java.io.ioexception;
import java.nio.file.*;
public class watchserviceexample {
    public static void main(string[] args) throws ioexception, interruptedexception {
        // 创建 watchservice
        watchservice watchservice = filesystems.getdefault().newwatchservice();
        // 注册监听指定的目录
        path dir = paths.get("c:\\users\\phoenix");
        dir.register(watchservice, standardwatcheventkinds.entry_create, standardwatcheventkinds.entry_modify, standardwatcheventkinds.entry_delete);
        while (true) {
            // 获取并处理事件
            watchkey key = watchservice.take();
            for (watchevent event : key.pollevents()) {
                system.out.println("event: "   event.kind()   " - "   event.context());
            }
            // 重置 key,继续监听
            if (!key.reset()) {
                break;
            }
        }
        watchservice.close();
    }
}

启动以上程序,程序就会监控我当前系统的用户目录,当我在用户目录创建文件并且编辑,删除,程序会输出以下内容:

event: entry_create - 新建 文本文档.txt
event: entry_delete - 新建 文本文档.txt
event: entry_create - helloworld.txt
event: entry_modify - helloworld.txt
event: entry_modify - helloworld.txt
event: entry_modify - helloworld.txt
event: entry_delete - helloworld.txt

pathmatcher 文件匹配

pathmatcher 是一个文件路径匹配接口,它可以帮助我们在遍历文件系统时,根据特定规则过滤出符合条件的文件或目录。它可以使用多种匹配语法(如 glob 和 regex),使得处理文件名或目录名的模式变得更加灵活和高效。pathmatcher 的使用场景包括:

  • 文件过滤:在搜索文件时,我们可能需要根据文件名或目录名的模式来过滤结果
  • 批量操作:当我们需要对文件系统中的一组文件或目录执行批量操作时,pathmatcher 可以帮助我们找到符合特定规则的文件或目录
  • 目录监控:可以结合 watchservice 对目录监控,然后通过 pathmatcher 过滤找出我们想要文件,如:.log 文件的创建,修改等

以下是一个简单示例代码:

import java.io.ioexception;
import java.nio.file.*;
import java.util.stream.stream;
public class pathmatcherexample {
    public static void main(string[] args) throws ioexception {
        // 创建 pathmatcher,使用 glob 语法:匹配所有以 .tmp 结尾的文件(临时文件)
        filesystem filesystem = filesystems.getdefault();
        pathmatcher matcher = filesystem.getpathmatcher("glob:*.tmp");
        // 在指定目录,找到匹配的文件,然后进行删除
        try (stream walk = files.walk(paths.get("path/to/directory"))) {
            walk.filter(path -> matcher.matches(path.getfilename())).foreach(path -> {
                system.out.println(path.getfilename());
                try {
                    files.delete(path);
                } catch (ioexception e) {
                    throw new runtimeexception(e);
                }
            });
        }
    }
}

上面的示例程序是通过 pathmatcher 匹配 .tmp 结尾的临时文件,然后进行删除的示例,结合 pathmatcher 可以轻松的完成一个清理临时文件的小程序。

读文件内容

上面的示例都是操作文件和目录,这里介绍一下如何读文件的内容,为了方便演示读取文件,先在 path/to/file.txt 相对目录下创建一个示例文本:

java is a high-level programming language.
python is an interpreted, high-level programming language.
javascript is a scripting language for web development.
c   is a general-purpose programming language.
rust is a systems programming language.

读文件主要用到 files 类的两个方法:

  • readalllines() 方法:一次性加载,主要用于读取小到中等的文件
  • lines() 方法:逐行读取,适用于大文件

小文件

readalllines() 适用于读取小到中等大小的文件,因为它会将整个文件内容加载到内存中,这个方法适用于在读取文件内容后立即处理整个文件的情况。使用示例:

public class linesexample {
    public static void main(string[] args) throws ioexception {
        // 读取全部文件
        list lines = files.readalllines(paths.get("path/to/file.txt"), standardcharsets.utf_8);
        // 对文件内容进行处理
        map wordfrequency = lines.stream()
                .flatmap(line -> arrays.stream(line.split("\\s ")))
                .map(string::tolowercase)
                .collect(collectors.groupingby(word -> word, collectors.counting()));
        system.out.println("word frequency:");
        wordfrequency.foreach((word, count) -> system.out.printf("%s: %d%n", word, count));
    }
}

大文件

lines() 方法: 使用场景:适用于读取大型文件,因为它不会一次性将整个文件内容加载到内存中。通过使用 java 8 的 stream api,可以在读取文件内容时同时处理每一行,从而提高处理效率。使用示例:

public class linesexample {
    public static void main(string[] args) throws ioexception {
        path filepath = paths.get("path/to/file.txt");
        // 逐行读取,并且在内容进行处理
        stream lines = files.lines(filepath);
        map wordfrequency = lines
                .skip(3)            // 跳过前 3 行
                .flatmap(line -> arrays.stream(line.split("\\s ")))
                .map(string::tolowercase)
                .collect(collectors.groupingby(word -> word, collectors.counting()));
        system.out.println("word frequency:");
        wordfrequency.foreach((word, count) -> system.out.printf("%s: %d%n", word, count));
        lines.close();
    }
}

输出结果:

word frequency:
rust: 1
a: 2
c  : 1
systems: 1
language.: 2
is: 2
programming: 2
general-purpose: 1

总结

在过去,java.io 包主要负责处理文件 i/o。但是它存在一些问题,例如性能不佳、api 不直观、文件元数据操作困难等。为了解决这些问题,后期的 java 版本引入了新的 java.nio.file 库。现在 java.nio.file 已经成为处理文件 i/o 的首选库。 pathfilesfilesystem 等工具类,可以更方便快捷的访问和操作文件系统。目前大多数的开发人员普遍认为 java.nio.file 比传统的 java.io 包更直观且易于使用。虽然 java.nio.file 库已经非常成熟,但是随着操作系统和文件系统的发展,我们仍然可以期待在未来的 java 版本中看到它的一些扩展和改进。

免费资源网 - https://freexyz.cn/
返回顶部
顶部
网站地图