1 文件和目录路径
Path 对象:表示一个文件或者目录的路径。父类 Paths
- Path.isAbsolute()
- Path.getFileName() // 获取文件名
- Path.getParent() // 除文件名其余路径
- Path.getRoot() // 获取根目录
- Path.toAbsolutePath();
- Path.toRealPath();
例:
Path p = Paths.get("C:", "path", "to", "nowhere", "NoFile.txt");
ap = p.toAbsolutePath();
rp = p.toRealPath();
// 生成URI
URI uri = p.toUri();
// URI转为Path
Path puri = Paths.get(uri);
*一定要注意:Path.toFile()转换为 File 对象, *
1.1 选取路径部分片段
Path p = Paths.get("PartsOfPaths.java").toAbsolutePath();
// 遍历打印每层路径,getName()索引Path的每一层,上限getNameCount()。
for(int i = 0; i < p.getNameCount(); i++)
System.out.println(p.getName(i));
p.startsWith("/"); // true
p.endWith("PartsOfPaths.java"); // true
p.endWith(".java"); // false endWith比较的是完整路径。
Path 也实现了 Iterable 接口,因此可以通过增加行 for-each 进行遍历。
1.2 路径分析
Path p = Paths.get("PathAnalysis.java").toAbsolutePath(); 常用分析 Path 相关方法 Files
- .exists(p) // 文件是否存在
- .isDirectory(p) // 是否是文件夹
- .isExecutable(p)
- .isReadable(p)
- .isWritable(p)
- .isHidden(p)
- .size(p)
- .getLastModifiedTime(p)
- .getOwner(p)
1.3 Paths 的修改
- Path.relativize() // 移除 Path 的根路径。
- Path.resolve() // 添加 Path 尾路径
- Path.getParent() // 删除 Path 尾路径
2 目录
Files 包含大部分目录和文件操作方法。但没有删除目录树相关方法。
删除目录树方法:
publicstaticvoid rmdir(Path dir) throwsIOException{
// 删除目录树的方法实现依赖于Files.walkFileTree(),即遍历每个子目录和文件
// java.nio.file.SimpleFileVisitor提供了所有方法的默认实现
Files.walkFileTree(dir, newSimpleFileVisitor<Path>() {
@Override
publicFileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throwsIOException{
Files.delete(file);
returnFileVisitResult.CONTINUE;
}
@Override
publicFileVisitResult postVisitDirectory(Path dir, IOException exc)
throwsIOException{
Files.delete(dir);
returnFileVisitResult.CONTINUE;
}
});
}
3 文件系统 FileSystem
FileSystem 为文件系统提供一个接口,是对象访问文件系统中文件和其他对象的工厂类。
FileSystem fsys = FileSystems.getDefaults(); // 默认文件系统
for(FileStore fs : fsys.getFileStores())
show("File Store", fs);
文件系统是几种类型对象的工厂。
- getPath 路径字符串,返回一个 Path 对象,用于定位和访问文件
- getPathMatcher 创建一个 PathMatcher 对象在路径上执行匹配操作
- getFileStores 返回一个 FileStore 对象迭代器
- newWatchService 方法可用于监视对象进行更改事件
- getUserPrincipalLookupService 查找用户或组
4 路径监听 WatchService
1 作用:设置一个线程对目录的更改做出响应。
2 创建过程:
- 初始化:创建当前 OS 平台的文件监控对象 WatchService
- 注册:监控器注册到指定文件节点(只监控该节点的子节点,孙子节点不监控),开启监控线程来监控 Path.register(WatchService watcher, WatchEvent.Kind)
监控事件类型
- ENTRY_CREATE:创建
- ENTRY_DELETE:删除
- ENTRY_MODIFY:修改
- 获取监控池:监控池是静态的,只有主动获取监控池中信息才会更新(一般在文件发生变化后获取监控池变化后的信息)
- 重置监控器:WatchKey.reset();
获取监控信息:其实就是获取新的监控池
- WatchKey WatchService.poll(); // 尝试获取下一个变化信息的监控池,如果没有变化则返回 null
- WatchKey WatchService.take(); // 尝试获取下一个变化信息的监控池,如果没有变化则一直等待(长时间监控)
// 创建test文件夹 和 Hello.txt文件
Path test = Paths.get("test");
if(Files.exists(test))
RmDir.rmdir(test);
if(!Files.exists(test))
Files.createDirectory(test);
Files.createFile(test.resolve("Hello.txt"));
// 从FileSystem中创建WatchService对象
WatchService watcher = FileSystems.getDefault().newWatchService();
// 注册到test节点以及操作类型
test.register(watcher, ENTRY_DELETE);
// delTxtFiles()方法用于删除.txt文件
// 创建一个线程,设置运行前等待时间,删除.txt
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);
}
5 文件查询 PathMatcher
路径匹配器 模式:
- glob:通配符模式(功能有限,但够用)
- regex:正则模式
// 获取test路径节点
Path test = Paths.get("test");
// 以.tmp,.txt结尾的文件
PathMatcher matcher = FileSystems.getDefault()
.getPathMatcher("glob:**/*.{tmp,txt}");
// 遍历文件下所有
Files.walk(test)
.filter(matcher::matches)
.forEach(System.out::println);
6 文件读取
先记录一下 IO 流方式读取文件
6.1 标准 IO :面向流
特点:
- 流是单向,只能读或者写
- 同步、阻塞
常用读写
- 字节读写(InputStream/OutputStream)
- 字符读取(FileReader/FileWriter)
- 行读取(BufferedReader/BufferedWriter)
6.2 NIO :面向缓冲、通道
特点:
- 使用通道和缓冲区来操作流
- 通道是双向的,可写也可读
- 非阻塞,一个线程可处理多个读写
- 适用高并发、大量连接
- 效率高
缓冲区:一块有位置、界限、容量的内存。
- capacity:容量,缓冲区的大小
- limit:限制,表示最大的可读写的数量
- position:当前位置,每当读写,当前位置都会加一
缓冲区常用方法:
- clear:将当前位置设置为 0,限制设置为容量,目的是尽最大可能让字节,由通道读取到缓冲中
- flip:当前位置置为限制,然后将当前位置置为 0,目的是将有数据部分的字节,由缓冲写入到通道中。通常用在读与写之间。
NIO 实现方式
- 直接缓冲区方式:
- 通道之前直接传输:FileChannel.transferFrom() -> 效率差
- 内在映像文件传输:FileChannel.map() -> 效率中(老版存在内存泄漏,GC 会失败)
- 非直接缓冲区方式:Buffer 效率最高
NIO 实现文件读写复制( 非直接缓冲区方式:效率最高)
- 创建输入输出流
File inf = newFile("in.txt");
File outf = newFile("out.txt");
FileInputStream inStream=newFileInputStream(inf);
FileOutputStream outStream=newFileOutputStream(outf);
获取通道
FileChannel inChannel = inStream.getChannel();
FileChannel outChannel = outStream.getChannel();
分配缓冲区大小
ByteBuffer buf = ByteBuffer.allocate(1024);
// 三个属性:容量、位置、限制
通过缓冲区读写文件
buf.clear(); // 清空缓冲区
while(inChannel.read(buf) != -1) {
buf.flip();//缓冲区当前位置限制,然后将当前位置置0
outChanel.write(buf);
buf.clear();
}
关闭文件流
outChanel.close();
inChannel.close();
fileInputStream.close();
fileOutputStream.close();
6.3 NIO 包中方法
拥抱 java.nio.file.Files 和 java.nio.file.Paths
Files 主要操作 Paths:
- Files.copy
- FIles.move
- Files.deleteIfExists
- Files.createDirectory
- Files.readAllLines:一次性读取整个文件,生成一个字符串链表(小文件)
- Files.readAllBytes:一次性读取整个文件,字节数组(小文件)
- Files.lines:一行一行(大文件)
- Files.write
// 过滤注释行,每行只打印一半
Files.readAllLines(Paths.get("test.java"))
.stream()
.filter(line -> !line.startsWith("//"))
.map(line -> line.substring(0, line.length() / 2))
.forEach(System.out::println);
原作者:耿直小博