文档章节

Java NIO之文件处理

士别三日
 士别三日
发布于 06/24 12:19
字数 2099
阅读 27
收藏 0

程序要操作本地操作系统的一个文件,可以分为以下三个部分:

  • 对文件位置的操作
  • 对文件的操作
  • 对文件内容的操作

其中,对文件内容的操作在 Java NIO之Channel 中已经有了介绍,通过FileChannel可以读/写文件内容。本文不做重复介绍,详情参考我的另一篇文章: Java NIO之Channel 。

1. 对文件位置的操作

在java.io中,有一个File类可以对文件位置、文件进行操作,对这两种操作没有区分开。对于操作文件位置的能力来说,File类不能很好地遍历目录,对于文件位置的操作非常不灵活,只能从父目录开始,一层一层地访问。

在java.nio中,提供了一个Path接口,这个接口专门处理文件位置,对文件本身操作不做处理。这个Path所代表的位置可以真实存在,也可以暂时先不存在。如果这个位置不存在,试图对文件、文件内容进行操作的时候,就会报错;但是对文件位置进行操作没有任何问题。

1.1 获取Path实例

Path是一个接口,不能直接实例化,最常用的实例化方法如下:

Path listing = Paths.get("Y:/path/usecase/example.txt");

对于Path的实例化,可以用绝对路径,也可以用相对路径,相对路径是指相对于代码运行时所在位置。如果文件位置在程序外的固定位置,就应该用绝对路径。如果文件位置在程序内,并且程序的运行时位置可能会变动,就应该用相对位置。

1.2 获取Path信息

Path接口提供了获取当前文件/目录名、父目录、根目录、子目录以及Path的元素个数:

Path listing = Paths.get("Y:/path/usecase/example.txt");
		
System.out.println("文件名称:"+listing.getFileName());
System.out.println("这个Path的元素个数:"+listing.getNameCount());
System.out.println("父路径:"+listing.getParent());
System.out.println("根路径:"+listing.getRoot());
System.out.println("从根路径开始数起,2个元素深度的子路径:"+listing.subpath(0, 2));

文件名称:example.txt
这个Path的元素个数:3
父路径:Y:\path\usecase
根路径:Y:\
从根路径开始数起,2个元素深度的子路径:path\usecase

如果你想通过当前位置,找到一个相对位置的文件,就非常方便,这在java.io中是做不到的。 

1.3 找到Path的真实路径

首先,加入创建Path实例的时候,用的是相对路径,有时间我们需要知道绝对路径在哪:

Path listing1 = Paths.get("example1.txt");
System.out.println("相对路径:"+listing1);
System.out.println("绝对路径:"+listing1.toAbsolutePath());

然后有时候我们会遇到用一个点代表当前目录的写法,这个写法在Path构建中是没有必要的。“./example2.txt”和“example2.txt”实际上代表的意思一样。遇到这种情况,在Path构建中就必须先正常化:

Path listing2 = Paths.get("./example1.txt");
System.out.println("相对路径:"+listing2);
System.out.println("绝对路径:"+listing2.toAbsolutePath());
Path listing3 = Paths.get("./example1.txt").normalize();
System.out.println("相对路径:"+listing3);
System.out.println("绝对路径:"+listing3.toAbsolutePath());

相对路径:.\example2.txt
绝对路径:Y:\item\workspaces\workspace-mp\alijishi\.\example2.txt
相对路径:example3.txt
绝对路径:Y:\item\workspaces\workspace-mp\alijishi\example3.txt

在Linux系统中,还有一种符号链接。比如/usr/logs只是一个符号链接,真实的位置在/application/logs,这个位置才是日志文件真正所在的位置。如何通过符号链接找到真实位置呢?通过toRealPath方法:

Path listing4 = Paths.get("example4.txt").toRealPath(options...);

1.4 对Path的合并和比较

Path prefix = Paths.get("Y:/prefix");
Path completePath = prefix.resolve("example4");
System.out.println("完整路径:"+completePath);
boolean start = completePath.startsWith("Y:/prefix");
System.out.println("completePath是否以Y:/prefix开头:"+start);
boolean end = completePath.endsWith("example3");
System.out.println("completePath是否以example3结尾:"+end);
Path listing5 = Paths.get("Y:/prefix/example5");
Path relativizePath = completePath.relativize(listing5);
System.out.println("completePath和listing5的相对位置:"+relativizePath);

完整路径:Y:\prefix\example4
completePath是否以Y:/prefix开头:true
completePath是否以example3结尾:false
completePath和listing5的相对位置:..\example5

1.5 在目录中查找文件

Path listing6 = Paths.get("Y:/path/usecase");
try(DirectoryStream<Path> stream = Files.newDirectoryStream(listing6,"*.txt")) {
	for(Path path:stream) {
		System.out.println(path);
	}
}

Y:\path\usecase\example.txt

DirectoryStream的第一个参数是目录位置,第二个参数是模式匹配(glob模式匹配),查询所有txt文件并打印出来。 

1.6 遍历目录树

Path listing7 = Paths.get("Y:/path/usecase");
Files.walkFileTree(listing7, new PrintFileVisitor());
private static class PrintFileVisitor extends SimpleFileVisitor<Path> {
	@Override
	public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
	    throws IOException
	{
	    Objects.requireNonNull(file);
	    Objects.requireNonNull(attrs);
	    System.out.println(file);
	    return FileVisitResult.CONTINUE;
	}
}

Y:\path\usecase\example.txt
Y:\path\usecase\mu1\example.txt
Y:\path\usecase\mu1\mu11\example.txt
Y:\path\usecase\mu1\mu12\example.txt
Y:\path\usecase\mu2\example.txt
Y:\path\usecase\mu3\example.txt
Y:\path\usecase\mu3\mu31\example.txt
Y:\path\usecase\mu3\mu32\example.txt
Y:\path\usecase\mu3\mu33\example.txt
Y:\path\usecase\mu3\mu33\mu331\example.txt

在java.nio中,有一个FileVisitor<T>接口。通过这个接口,可以实现目录树的遍历。这个接口有四个方法:

  • FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs) 访问上层目录
  • FileVisitResult visitFile(T file, BasicFileAttributes attrs) 访问当前目录文件
  • FileVisitResult visitFileFailed(T file, IOException exc)  访问文件失败怎么处理
  • FileVisitResult postVisitDirectory(T dir, IOException exc) 访问下层目录

java.nio提供了一个默认实现SimpleFileVisitor<T>,这个类实现了遍历功能,如果你想在遍历的过程中加点操作,就可以直接继承SimpleFileVisitor。上面的例子就是在遍历目录的时候,看到文件就打印出来。

2 对文件的操作

上面介绍的Path接口只是对文件位置进行操作,java.nio还提供了一个Files工具类,专门操作文件。可以取代java.io中的File类。

2.1 创建和删除文件

创建文件的时候还可以指定文件属性:

Path listing8 = Paths.get("Y:/path/usecase/created.txt");
//UNIX系统
Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-rw-rw-");
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms);
Files.createFile(listing8,attr);
Files.delete(listing8);

2.2 文件复制和移动

Path listing9 = Paths.get("Y:/path/usecase/example.txt");
Path listing10 = Paths.get("Y:/path/usecase/copy/example.txt");
Files.copy(listing9,listing10,StandardCopyOption.COPY_ATTRIBUTES,StandardCopyOption.REPLACE_EXISTING);

 移动文件的时候,有三个选项可以配置:

  • COPY_ATTRIBUTES是指复制文件的时候把文件属性也一起复制过去;
  • ATOMIC_MOVE是指确保两边操作都成功,否则回滚。在复制的时候用不着;
  • REPLACE_EXISTING是指如果目标文件存在该文件,就覆盖。

2.3 文件属性

每个文件都有它的属性,比如访问权限控制、修改时间、文件大小、文件类型、是不是目录、是不是符号链接……

文件属性分为通用属性和操作系统专有属性。通用属性在java.nio有一个BasicFileAttributes接口,通过以下代码可以获取:

BasicFileAttributes attr = Files.readAttributes(listing9, BasicFileAttributes.class);

在Files类中,对每个通用属性都提供了对应的获取方法,这里不一一探讨。

对于操作系统专有属性,java.nio允许操作系统开发者提供实现FileAttributeView接口和BasicFileAttributes接口,像PosixFileAttributes就是Unix系统专有的属性,这里不做探讨。但是设置文件属性的时候一定要注意系统的兼容性。

2.4 监控文件的变化

在java.nio中,提供了一个WatchService接口,WatchService的实例可以监控文件或目录的变化,监控到变化的时候就会返回一个事件。这在很多重要场景中使用,比如对应用的环境变量监控。

try {
	WatchService watcher = FileSystems.getDefault().newWatchService();
	Path dir = Paths.get("Y:/path/usecase");
	//检测变化
	WatchKey key = dir.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);
			
	while(true) {
		key = watcher.take();
				
		for(WatchEvent<?> event:key.pollEvents()) {
			if(event.kind()==StandardWatchEventKinds.ENTRY_MODIFY) {
				System.out.println("dir changed!");
			}
		}
	}
} catch (Exception e) {
			
}

这个也是用观察者模式来实现。监控器WatchKey是个阻塞对象,可以监控多个目录/文件的多个不同事件类型。有点类似于网络IO中的Selector,也是个IO多路复用。下面是可以监控的四种事件:

  • ENTRY_CREATE 创建事件
  • ENTRY_DELETE 删除事件
  • ENTRY_MODIFY 修改事件
  • OVERFLOW 磁盘不够事件

3.总结

本文并没有把相关的类和方法全部覆盖,重点讲了Path和Files的用法,他们分别处理文件位置和文件。至于文件内容的操作超出了本文的范围。掌握了这三个部分的操作,就完整地掌握了对文件的操作。 

© 著作权归作者所有

共有 人打赏支持
上一篇: Java NIO之字符集
下一篇: Java可扩展IO
士别三日

士别三日

粉丝 38
博文 30
码字总数 43081
作品 0
深圳
程序员
私信 提问
java NIO:IO与NIO的区别

一、概念 NIO即New IO,这个库是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多。在Java API中提供了两套NIO,一套是针对标...

盼望明天
09/11
0
0
Java NIO原理 图文分析及代码实现

Java NIO原理图文分析及代码实现 前言: 最近在分析hadoop的RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术...

囚兔
2015/04/29
0
0
Java NIO原理图文分析及代码实现

前言: 最近在分析hadoop的RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。可以参考:http://baik...

SunnyWu
2014/11/05
0
1
Java NIO AsynchronousFileChannel

原文链接 , 原文作者:Jakob Jenkov, 翻译:Neil Hao 在Java 7,AsynchronousFileChannel 被添加到了Java NIO中。使用AsynchronousFileChannel可以实现异步地读取和写入文件数据。 创建一个A...

Neil_Hao
01/20
0
0
Java NIO系列教程(一) Java NIO 概述

Java NIO 由以下几个核心部分组成: Channels Buffers Selectors 虽然Java NIO 中除此之外还有很多类和组件,但在我看来,Channel,Buffer 和 Selector 构成了核心的API。其它组件,如Pipe和...

只想一个人静一静
2014/02/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

系统维护和tcp连接

查看系统负载 1 w 命令 w命令用于显示系统当前负载 和系统已登录的用户. 查看系统CPU 和核数: cat /proc/cpuinfo| grep 'cpu cores' 第一行显示 :04:41:16 up 8:56, 1 user, load average: 0...

Fc丶
27分钟前
0
0
Mac Pro 下安装 Snappy 压缩工具

snappy 我这里就不做介绍了,直接可以移步 https://github.com/google/snappy/tree/master 查看源码及说明信息。 我这里下载 :https://github.com/google/snappy/releases/download/1.1.4/...

Ryan-瑞恩
30分钟前
1
0
iframe里弹出的层显示在整个网页上

通过在iframe页面添加js脚本,动态给父窗体创建一个div,然后设置让其显示在最顶层这样就可以了 在文件夹中创建两个文件,一个iframe页面,一个父页面index。

少年已不再年少
44分钟前
1
0
聊聊storm trident spout的_maxTransactionActive

序 本文主要研究一下storm trident spout的_maxTransactionActive MasterBatchCoordinator storm-core-1.2.2-sources.jar!/org/apache/storm/trident/topology/MasterBatchCoordinator.java ......

go4it
53分钟前
1
0
js时间函数getTime() 在苹果手机上返回NaN的问题

一、出现问题 var newStartDate = new Date('2017-08-30');var newStartTime = newStartDate.getTime(); 获取到的时间戳,在Android手机正常,在IPhone中返回NaN。 问题说明: 在苹果手机...

tianma3798
55分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部