Java中使用IO Stream直接操作文件的,但是当我们使用这些的时候,往往要经过很多步骤,比如打开文件,关闭文件。很多时候,我们会记得打开文件,但是最后还是一个close()过程,我们总是容易忽略。
一.文件操作Util类
Guava建议我们定义所有的IO实体时都使用InputSupplier和OutputSupplier进行包装,这样,我们就不用考虑那么多事情了,简化了操作。
我们知道java IO包里面将文件流分为字节流(InputStream or OutputStream)和字符流(Reader or Writer),guava里面的ByteStreams and CharStreams分别提供了util方法,用来对字节流和字符流进行处理。主要包含的方法如下:
此外,Files类提供了很多static的方法来对文件进行操作,其中有2个方法可以把文件使用InputSupplier或者OutputSupplier进行封装,使用封装后的对象就不会忘记关闭文件了。
static InputSupplier<T> newInputStreamSupplier(File) 可以将file对象使用InputSupplier进行封装。
static OutputSupplier<T> newOutputStream(File) 可以将file对象使用OutputSupplier进行封装。
其实Files功能和ByteStreams and CharStreams差不多,其他的重要方法包括,:
toString():读取文件内容
copy():文件内容拷贝,包含多种参数形式
move():文件移到,可以重命名
getFileExtension():获取文件后缀名
getNameWithoutExtension():返回不含后缀的文件名
readLines(File, Charset, LineProcessor):每读取一行就交给LineProcessor进行处理
map(File):把文件映射到内存区块,返回java.nio. MappedByteBuffer
createParentDir(File):如果父目录不存在,则创建之。
simplifyPath(String):把路径格式化
二.关闭文件
在JDK7以前,对文件操作,我们需要时刻注意用完后关闭文件。
InputStream in = null;
try {
in = openInputStream();
OutputStream out = null;
try {
out = openOutputStream();
// do something with in and out
} finally {
if (out != null) {
out.close();
}
}
} finally {
if (in != null) {
in.close();
}
}
可以看到,这段代码真是太复杂了,一不小心就出错了。
可喜的是JDK7有了新的措施来解决这类问题,那就是try-with-resources 语句,如:
try (
java.util.zip.ZipFile zf =
new java.util.zip.ZipFile(zipFileName);
java.io.BufferedWriter writer =
java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
) {
// Enumerate each entry
for (java.util.Enumeration entries =
zf.entries(); entries.hasMoreElements();) {
// Get the entry name and write it to the output file
String newLine = System.getProperty("line.separator");
String zipEntryName =
((java.util.zip.ZipEntry)entries.nextElement()).getName() +
newLine;
writer.write(zipEntryName, 0, zipEntryName.length());
}
}
基本语法是:try(…) {} ,try里面所有实现接口java.lang.AutoCloseable,包括java.io.Closeable的对象,在{}的语句块执行完毕后都会自动的close。
那JDK7以前的版本怎么办呢,guava给我们提供了方法。我们可以把Closeable 的对象注册到Closer对象上,资源使用完毕后,调用closer的close方法,就可以把所有注册了的资源安全的close掉。这个方法虽然没有try-with-resources好用,但是比起传统的jdk做法,要好很多了。
Closer closer = Closer.create();
try {
InputStream in = closer.register(openInputStream());
OutputStream out = closer.register(openOutputStream());
// do stuff with in and out
} catch (Throwable e) { // must catch Throwable
throw closer.rethrow(e);
} finally {
closer.close();
}
三.Source和Sink
此外,guava还提供了Source和Sink来对文件流进行操作。
Files提供了方法,从File对象得到Source和Sink。
stati c ByteSource Files.asByteSource
static CharSource Files.asCharSource
static ByteSink Files.asByteSink
static CharSink Files.asCharSink
Source实现了InputSupplier,表示可读的输入流,但是跟InputSupplier不同,它是一个不可变化的supplier实例。
Source分为ByteSource 和CharSource,分别用来处理字节流和字符流。
Source提供了方法openStream() 返回一个新创建的输入流,可以使用这个流来读取文件内容,读取完毕后调用方要负责关闭掉。
此外,Source还提供了一些快捷操作的方法,如copyTo,调用这样的方法,操作完毕后会自动关闭文件,不用考虑手动关闭文件的问题。
与Source对应,Sink实现了OutputSupplier,表示可写的输出流,它也是一个不可变的supplier实例。
Sink也分为ByteSink和CharSink,分别用来处理字节流和字符流。
Sink也提供了方法openStream()返回一个新创建的输出流,可以使用这个流来写文件,完成后调用方要记得关闭掉流。
Sink里面有些快捷操作方法,如writeLines,writeFrom操作完成后也是会自动关闭文件的。
Source和Sink用起来还是很方便的,如下面一句话就可以实现文件复制了,完全不用考虑文件的关闭。
Files.asByteSource(new File("d:/1.txt")).copyTo(Files.asByteSink(new File("d:/2.txt"),FileWriteMode.APPEND));