Google Guava 快速入门 —— 文件流

Google Guava 快速入门.jpg

一、字节流和字符流

Guava 使用术语”流” 来表示可关闭的,并且在底层资源中有位置状态的I/O数据流。

  • 字节流指的是 InputStream 或 OutputStream

  • 字符流指的是 Reader 或 Writer(虽然他们的接口 Readable 和 Appendable 被更多地用于方法参数)。相应的工具方法分别在 ByteStreams 和 CharStreams 中。

大多数 Guava 流工具一次处理一个完整的流,并且/或者为了效率自己处理缓冲。还要注意到,接受流为参数的Guava方法不会关闭这个流:关闭流的职责通常属于打开流的代码块。

其中的一些工具方法列举如下:

ByteStreamsCharStreams
byte[] toByteArray(InputStream)String toString(Readable)
N/AList<String> readLines(Readable)
long copy(InputStream, OutputStream)long copy(Readable, Appendable)
void readFully(InputStream, byte[])N/A
void skipFully(InputStream, long)void skipFully(Reader, long)
OutputStream nullOutputStream()Writer nullWriter()

二、源与汇

通常我们都会创建I/O工具方法,这样可以避免在做基础运算时总是直接和流打交道。例如:Guava有 Files.toByteArray(File) 和 Files.write(File, byte[])

然而,流工具方法的创建经常最终导致散落各处的相似方法,每个方法读取不同类型的源或写入不同类型的汇[sink]。例如:Guava中的 Resources.toByteArray(URL) 和 Files.toByteArray(File) 做了同样的事情,只不过数据源一个是URL,一个是文件。

为了解决这个问题,Guava有一系列关于源与汇的抽象。源或汇指某个你知道如何从中打开流的资源,比如 File 或 URL。源是可读的,汇是可写的。此外,源与汇按照字节和字符划分类型。

操作字节字符
ByteSourceCharSource
ByteSinkCharSink

源与汇API的好处是它们提供了通用的一组操作。比如,一旦你把数据源包装成了 ByteSource,无论它原先的类型是什么,你都得到了一组按字节操作的方法。

1、创建源与汇

Guava 提供了若干源与汇的实现:

字节字符
Files.asByteSource(File)Files.asCharSource(File, Charset)
Files.asByteSink(File, FileWriteMode...)Files.asCharSink(File, Charset, FileWriteMode...)
Resources.asByteSource(URL)Resources.asCharSource(URL, Charset)
ByteSource.wrap(byte[])CharSource.wrap(CharSequence)
ByteSource.concat(ByteSource...)CharSource.concat(CharSource...)
ByteSource.slice(long, long)N/A
N/AByteSource.asCharSource(Charset)
N/AByteSink.asCharSink(Charset)

此外,你也可以继承这些类,以创建新的实现。

注:把已经打开的流(比如InputStream)包装为源或汇听起来是很有诱惑力的,但是应该避免这样做。源与汇的实现应该在每次 openStream() 方法被调用时都创建一个新的流。始终创建新的流可以让源或汇管理流的整个生命周期,并且让多次调用 openStream() 返回的流都是可用的。此外,如果你在创建源或汇之前创建了流,你不得不在异常的时候自己保证关闭流,这压根就违背了发挥源与汇API优点的初衷。

2、使用源与汇

一旦有了源与汇的实例,就可以进行若干读写操作。

(1)通用操作

所有源与汇都有一些方法用于打开新的流用于读或写。默认情况下,其他源与汇操作都是先用这些方法打开流,然后做一些读或写,最后保证流被正确地关闭了。这些方法列举如下:

  • openStream():根据源与汇的类型,返回 InputStreamOutputStreamReader 或者 Writer

  • openBufferedStream():根据源与汇的类型,返回 InputStreamOutputStreamBufferedReader 或者 BufferedWriter,返回的流保证在必要情况下做了缓冲。

(2)源操作
字节源字符源
byte[] read()String read()
N/AImmutableList<String> readLines()
N/AString readFirstLine()
long copyTo(ByteSink)long copyTo(CharSink)
long copyTo(OutputStream)long copyTo(Appendable)
long size() (in bytes)N/A
boolean isEmpty()boolean isEmpty()
boolean contentEquals(ByteSource)N/A
HashCode hash(HashFunction)N/A
(3)汇操作
字节汇字符汇
void write(byte[])void write(CharSequence)
long writeFrom(InputStream)long writeFrom(Readable)
N/Avoid writeLines(Iterable<? extends CharSequence>)
N/Avoid writeLines(Iterable<? extends CharSequence>, String)

3、范例

//Read the lines of a UTF-8 text file
ImmutableList<String> lines = Files.asCharSource(file, Charsets.UTF_8).readLines();

//Count distinct word occurrences in a file
Multiset<String> wordOccurrences = HashMultiset.create(
        Splitter.on(CharMatcher.WHITESPACE)
            .trimResults()
            .omitEmptyStrings()
            .split(Files.asCharSource(file, Charsets.UTF_8).read()));

//SHA-1 a file
HashCode hash = Files.asByteSource(file).hash(Hashing.sha1());

//Copy the data from a URL to a file
Resources.asByteSource(url).copyTo(Files.asByteSink(file));

三、文件操作

除了创建文件源和文件的方法,Files类还包含了若干你可能感兴趣的便利方法。

方法名说明
createParentDirs(File)必要时为文件创建父目录
getFileExtension(String)返回给定路径所表示文件的扩展名
getNameWithoutExtension(String)返回去除了扩展名的文件名
simplifyPath(String)规范文件路径,并不总是与文件系统一致,请仔细测试
fileTreeTraverser()返回TreeTraverser用于遍历文件树

四、部分示例

//guava提供了一个源与汇的概念对应读写字节字符共有4个类ByteSource,CharSource,ByteSink,CharSink
URL url = new URL("this the resource url");
File file = new File("your file path");
Resources.toByteArray(url);//从url中获得字节数组
Files.toByteArray(file);//从文件中获得字节数组
CharSource charSource = Files.asCharSource(file, Charsets.UTF_8);//获得字符源
ByteSource byteSource = Files.asByteSource(file);//获得字节源
Resources.asCharSource(url, Charsets.UTF_8);//从url中获得字符源
Resources.asByteSource(url);//从url中获得字节源

CharSink charSink = Files.asCharSink(file, Charsets.UTF_8, FileWriteMode.APPEND);//获得字符汇 以append追加方式(覆盖方式可以省略)
ByteSink byteSink = Files.asByteSink(file, FileWriteMode.APPEND);//或者字节汇 以append追加方式(覆盖方式可以省略)

byteSource.asCharSource(Charsets.UTF_8);//字节源转字符源
byteSink.asCharSink(Charsets.UTF_8);//字节汇转字符汇
byte[] bytes = byteSource.read();//读取出byte[]
String string = charSource.read();//读取出String
byteSource.copyTo(byteSink);//字节源copy到汇 可以使用OutputStream
charSource.copyTo(charSink);//字符源copy到汇 可以使用Appendable
byteSink.write(bytes);//写入字节
charSink.write(string);//写入字符(使用的是CharSequence,可以用String)

//案例
//逐行读取 以utf-8编码
ImmutableList<String> lines = Files.asCharSource(file, Charsets.UTF_8).readLines();

//读取单词
Multiset<String> wordOccurrences = HashMultiset.create(
        //以空格拆分
        Splitter.on(CharMatcher.whitespace())
                .trimResults()
                .omitEmptyStrings()
                .split(Files.asCharSource(file, Charsets.UTF_8).read()));

                //获取文件按照sha1生成的hash码
HashCode hash = Files.asByteSource(file).hash(Hashing.sha1());

//File的工具类Files
//将url资源copy到file中
Resources.asByteSource(url).copyTo(Files.asByteSink(file));

五、相关文章



未经允许请勿转载:程序喵 » Google Guava 快速入门 —— 文件流

点  赞 (0) 打  赏
分享到: