笔记:I/O流-文件操作
Java库中使用 Path 和 Files 类封装了在用户机器上处理文件系统所需要的所有功能,可以使用Paths来获取一个具体的Path对象,来表示具体的路径。
- 路径
Path表示的是一个目录名序列,其后还可以跟着一个文件名,以根路径开始的路径是绝对路径;否则就是相对路径,示例代码如下:
Path absolute = Paths.get("E:\\", "VirtualMachines", "CentOS 64 位", "CentOS 64 位.vmdk");
System.out.println("absolute path " + absolute);
Path relative = Paths.get("/opt");
System.out.println("relative path " + relative);
组合和解析路径是常见操作,调用 p.resolve(q)将按照下列规则返回一个路径:
- 如果q是绝对路径,则返回结果就是 q
- 否则,根据文件系统的规则,将 p 后面跟着 q 作为结果
Path relative = Paths.get("/opt");
System.out.println("relative path " + relative);
Path workRelative = relative.resolve("work");
System.out.println("workRelative path " + workRelative);
输出路径 workRelative 结果为 " \opt\work",还有一个很方便的方法 resolveSibling ,他通过解析指定路径的父路径产生其兄弟路径,示例代码如下:
Path tempPath = workRelative.resolveSibling("temp");
System.out.println("resolveSibling path " + tempPath);
输出路径 tempPath 结果为"\opt\temp",如果需要把路径产生相对路径,可以调用方法 relativize,该方法是返回指定路径之间的相对路径,示例代码如下:
Path relativize = workRelative.relativize(tempPath);
System.out.println("relativize source path \"" + workRelative + "\" relativize path \""
+ tempPath + "\" result path \"" + relativize + "\"");
输出结果为"relativize source path "\opt\work" relativize path "\opt\temp" result path "..\temp"",可以看到返回了相对化路径,与其相反操作的方法 normalize方法,将移除相对化,示例代码如下:
Path normalizeSourcePath = Paths.get("/opt","work/../test");
System.out.println("normalize source path \"" + normalizeSourcePath + "\" result path \""
+ normalizeSourcePath.normalize() + "\"");
输出结果为"normalize source path "\opt\work\..\test" result path "\opt\test""。
- 文件
- 读写文件
Files类可以使得普通文件操作变得快捷,例如,使用 readAllBytes 方法可以很容易的读取文件的所有内容:
byte{] bytes = Files.readAllBytes(path);
如果想将文件当作字符串读入,那么可以在调用 readAllBytes 方法后使用String类型实例化,如下代码:
String content = new String(bytes,charset);
如果想把文件当作行序列读入,那么可以调用如下代码:
List<String> lines = Files.readAllLines(path);
如果希望写出字符串到文件,可以调用如下代码:
Files.write(path,content.getBytes(charset));
// 向文件追加内容
Files.write(path,content.getBytes(charset),StandardOpenOption.APPEND);
Files.write(path,lines);
以上方法适用于处理中等长度的文件,如果要处理的文件长度比较大,或者是二进制文件,那么应该使用熟知的流或者读入器/写出器,示例代码如下:
InputStream in = Files.newInputStream(path);
OutputStream out = Files.newOutputStream(path);
Reader in = Files.newBufferedReader(path);
Writer out = Files.newBufferedWriter(path);
- 复制、移动和删除文件
将文件从一个位置复制到另一个位置可以直接调用
Files.copy(fromPath,toPath);
移动文件可以调用
Files.move(fromPath,toPath);
如果目标路径已经存在,那么复制或移动将失败,如果想要覆盖已经有的目标路径,可以使用 REPLACE_EXISTING 选项,可以使用 COPY_ATTRIBUTES选项来复制所有的文件属性,示例代码如下:
Files.copy(fromPath,toPath,StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.COPY_ATTRIBUTES);
可以将移动操作定义为原子性的,这样可以保证要么移动文件完成,要么文件保持在原地,可以使用StandardCopyOption.ATOMIC_MOVE,代码如下:
Files.move(fromPath,toPath,StandardCopyOption.ATOMIC_MOVE);
最后删除文件可以调用 delete 方法,如果文件不存在则会抛出异常,代码如下:
Files.delete(path);
如果不希望在文件不存在时抛出异常,则可以i使用 deleteIfExists 方法,该方法也可以用来删除空目录,代码如下:
boolean deleted = Files.deleteIfExists(path);
- 创建文件和目录
创建新目录可以调用 createDirectory 方法,该方法的路径参数除最后一个部件外,其他部分都必须时已经存在的,如果要创建路径中间的目录,应该使用 createDirectories 方法,如果创建一个空文件可以使用 createFile 方法,如果文件已经存在,则会抛出异常,示例代码如下:
Files.createDirectory(path);
Files.createDirectories(path);
Files.createFile(path);
有些便捷的方法可以用来在给定位置或者系统指定位置创建临时文件或者临时目录,代码如下:
Path tempDirFile = Files.createTempFile(dirPath, "tt", ".tmp");
Path systemTempFile = Files.createTempFile("sys", ".tmp");
System.out.println("Temp1=" + tempDirFile + "\tTemp2=" + systemTempFile);
Path tempDirPath = Files.createTempDirectory(dirPath, "dir");
Path systemTempDirPath = Files.createTempDirectory("sys");
System.out.println("Temp1=" + tempDirPath + "\tTemp2=" + systemTempDirPath);
- 获取文件信息
Files类提供了一些静态方法来获取文件信息和属性,方法如下:
- exists:检查路径文件或目录是否存在
- isHidden:检查路径文件或目录是否是隐藏的
- isReadable:检查路径文件或目录是否只读
- isWritable:检查路径文件是否可写
- isExecutable:检查路径文件是否可执行的
- isRegularFile:检查路径文件是否为常规文件
- isDirectory:检查路径是否为目录
- isSymbolicLink:检查路径是否为符号连接文件
- size:返回文件的字节数
- getOwner:返回文件的拥有者
所有的文件系统都会报告一个基本属性集,他们被封装在 BasicFileAttributes接口,基本文件属性包括:
- 创建文件、最后一次访问以及最后一次修改文件的时间,这些时间都表示为 java.nio.file.attribute.FileTime 类
- 文件是常规文件、目录或者符号连接,或者三者都不是
- 文件尺寸
要获取这些属性,调用代码如下:
BasicFileAttributes basicAttributes = Files.readAttributes(filePath,BasicFileAttributes.class);
- 迭代目录中的文件
Files类设置了一个方法,他可以产生一个Iterable对象,通过整个对象可以遍历目录下的所有文件,示例代码如下:
dirPath = Paths.get("d:\\", "directories", "TempDir");
try (DirectoryStream<Path> entries = Files.newDirectoryStream(dirPath)) {
for (Path p : entries) {
BasicFileAttributes attributes = Files.readAttributes(p, BasicFileAttributes.class);
System.out.println("path=" + p + "\tisDirectory=" + attributes.isDirectory()
+ "\tsize=" + attributes.size());
}
}
try语句块用来确保目录流可以被正确关闭,访问目录中的项并没有具体的顺序,还可以使用glob模式来过滤文件,示例代码如下:
try (DirectoryStream<Path> entries = Files.newDirectoryStream(dirPath,"*.java")) {
// 处理代码
}
模式 | 描述 | 示例 |
* | 匹配路径组成部分中0个或多个字符 | *.java 匹配当前目录中的所有 java 文件 |
** | 匹配跨目录边界的0个或多个字符 | **.java 匹配在所有子目录中的java文件 |
? | 匹配一个字符 | ???.java 匹配所有3个字符的java文件 |
[…] | 匹配一个字符集合,可以使用连线符[0-9]和取反符[!0-9] | test[0-9].java 匹配testx.java,其中x表示数字0-9 |
{…} | 匹配由逗号隔开的多个可选项之一 | *.{java,class}匹配所有的java文件和类class文件 |
\ | 转义上述任意模式中的字符 | *\** 匹配所有文件名称包含 * 的文件 |
如果想要访问某个目录的所有子孙成员,可以转而调用 walkFileTree 方法,并向其传递一个 FileVisitor 类型的对象,该对象用来处理文件通知,说明如下:
在遇到一个文件或者目录时,会通知 visitFile 方法
在一个目录被处理前,会通知 preVisitDirectory 方法
在一个目录被处理后,会通知 postVisitDirectory方法
在试图访问文件或者目录出现错误,会通知 visitFileFailed 方法,并根据其返回结果来决定后续操作,返回结果有:
- FileVisitResult.CONTINUE:继续访问下一个文件
- FileVisitResult.SKIP_SUBTREE:继续访问,但是不再访问整个目录下的任何项
- FileVisitResult.SKIP_SIBLINGS:继续访问,但是不再访问整个文件的兄弟文件
- FileVisitResult.TERMINATE:终止访问
示例代码如下:
Files.walkFileTree(dirPath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
System.out.println("preVisitDirectory path=" + dir);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
BasicFileAttributes attributes = Files.readAttributes(file, BasicFileAttributes.class);
System.out.println("visitFile path=" + file + "\tisDirectory=" + attributes.isDirectory()
+ "\tsize=" + attributes.size());
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
System.out.println("visitFileFailed path=" + file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
System.out.println("preVisitDirectory path=" + dir);
return FileVisitResult.CONTINUE;
}
});
- ZIP文件系统
Paths类会在默认文件系统中查找路径,即在用户本地磁盘中的文件,也可以在别的文件系统,例如ZIP文件系统,示例代码:
Path zipPath = Paths.get("C:\\Users\\Downloads", "commons-logging-1.2-bin.zip");
FileSystem fs = FileSystems.newFileSystem(zipPath, null);
将创建一个文件系统,包含ZIP文档中的所有文件,如果知道文件名称那么就可以从ZIP文文档中复制出整个文件,示例代码如下:
Files.copy(fs.getPath("commons-logging-1.2\\LICENSE.txt"),Paths.get("d:\\LICENSE.txt"));
其中,fs.getPath 对于任意文件系统来说,都和Paths.get 类似,如果要遍历ZIP文档中的所有文件,同样可以使用 walkFileTree 方法来处理,示例代码:
Files.walkFileTree(fs.getPath("/"), new SimpleFileVisitor<Path>() {
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
System.out.println("Zip File " + file);
return FileVisitResult.CONTINUE;
}
});