Java Reader类
Java Reader类java.io.Reader是Java IO API中所有Reader类的基类。 JavaReader
类似于Java InputStream,不同之处在于它是基于字符而不是基于字节的。换句话说,Java的"阅读器"用于读取文本(字符),而" InputStream"则用于读取原始字节。
Reader和来源
"Reader"通常连接到某些数据源,例如文件,字符数组,网络套接字等。这在Java IO概述文本中也有更详细的说明。
Unicode中的字符
如今,许多应用程序都使用Unicode(UTF-8或者UTF-16)存储文本数据。在UTF-8中,可能需要一个或者多个字节来表示单个字符。在UTF-16中,每个字符用2个字节表示。因此,在读取文本数据时,数据中的单个字节可能不对应于UTF中的一个字符。如果我们只是一次通过InputStream读取UTF-8数据中的一个字节,然后尝试将每个字节转换为char类型,则可能无法获得预期的文本。
为了解决这个问题,我们有Reader
类。 " Reader"类能够将字节解码为字符。我们需要告诉"阅读器"要解码的字符集。当我们实例化Reader
时(实际上,当我们实例化其子类之一时)便完成了此操作。
Java Reader子类
通常,我们将直接使用Reader
子类,而不是直接使用Reader
。 Java IO包含许多Reader
子类。以下是JavaReader
子类的列表:
- InputStreamReader
- CharArrayReader
- 文件阅读器
- PipedReader
- BufferedReader
- FilterReader
- PushbackReader
- LineNumberReader
- 字符串阅读器
这是一个创建JavaFileReader
的示例,它是JavaReader
的子类:
Reader reader = new FileReader("/path/to/file/thefile.txt");
从Reader中读取字符
JavaReader
的read()
方法返回一个整数,该整数包含下一个读取的字符的char
值。如果read()
方法返回-1,则Reader
中没有更多的数据可读取,因此可以将其关闭。也就是说,-1作为int值,而不是-1作为byte或者char值。这里有区别!
这是一个从JavaReader
读取所有字符的示例:
Reader reader = new FileReader("/path/to/file/thefile.txt"); int theCharNum = reader.read(); while(theCharNum != -1) { char theChar = (char) theCharNum; System.out.print(theChar); theCharNum = reader.read(); }
注意,该代码示例如何首先从JavaReader
中读取单个字符,并检查char数值是否等于-1. 如果没有,它将处理该char并继续读取,直到从Reader read()方法返回-1为止。
从Reader读取字符数组
JavaReader
类还具有一个read()
方法,该方法采用一个char
数组作为参数,以及一个起始偏移量和长度。 char数组是read()方法将字符读入的数组。 offset参数是在char数组中的read()方法应该开始读入的位置。 length参数是read()方法应从偏移量开始并向前读取到char数组中的字符数。这是一个使用JavaReader
将字符数组读入char
数组的示例:
Reader reader = new FileReader("/path/to/file/thefile.txt"); char[] theChars = new char[128]; int charsRead = reader.read(theChars, 0, theChars.length); while(charsRead != -1) { System.out.println(new String(theChars, 0, charsRead)); charsRead = reader.read(theChars, 0, theChars.length); }
" read(char [],offset,length)"方法返回读取到" char"数组中的字符数,如果没有更多的字符要在" Reader"中读取,则返回-1,例如,如果已达到"阅读器"所连接的文件。
Reader的性能
一次读取一个字符数组比一次从JavaReader
读取单个字符要快。通过读取字符数组而不是一次读取单个字符,该差异很容易成为性能提高的10倍或者更多。
所获得的确切速度取决于读取的char
数组的大小以及运行代码的计算机的OS,硬件等。在决定之前,我们应该研究目标系统的硬盘缓冲区大小等。但是,如果缓冲区大小为8KB或者更高,则可以实现很好的加速。但是,一旦char
数组超出了底层操作系统和硬件的容量,我们将不会从更大的char
数组中获得更大的加速。
我们可能必须尝试不同的字节数组大小并测量读取性能,以找到最佳的" char"数组大小。
通过BufferedReader进行透明缓冲
我们可以使用Java BufferedReader从Reader
中添加透明的,自动的读取和缓冲字节数组的函数。 BufferedReader
从底层的Reader
读取一大堆char
到char
数组中。然后,我们可以从" BufferedReader"中逐个读取字节,并且仍然可以从读取" chars"数组而不是一次读取一个字符中获得很大的提速。这是将JavaReader
包装在BufferedReader
中的示例:
Reader input = new BufferedReader( new FileReader("c:\data\input-file.txt"), 1024 * 1024 /* buffer size */ );
注意," BufferedReader"是" Reader"的子类,可以在任何可以使用" Reader"的地方使用。
跳过字符
JavaReader
类具有一个名为skip()
的方法,该方法可用于跳过我们不想读取的输入中的许多字符。我们将要跳过的字符数作为参数传递给skip()
方法。这是一个从JavaReader
跳过字符的示例:
long charsSkipped = reader.skip(24);
这个例子告诉JavaReader
跳过Reader
中接下来的24个字符。 skip()
方法返回被跳过的实际字符数。在大多数情况下,该数字与我们请求的跳过数字相同,但是如果"阅读器"中剩余的字符少于我们请求跳过的数字,则返回的跳过字符数可以小于我们请求的字符数跳过了。
关闭Reader
当我们从"阅读器"中读取完字符后,我们应该记得将其关闭。关闭阅读器是通过调用其close()方法完成的。这是关闭Reader
的样子:
reader.close();
我们还可以使用Java 7中引入的Java try with resources构造。这是如何通过try-with-resources构造使用和关闭" InputStreamReader"外观的方法:
try(Reader reader = new FileReader("/path/to/file/thefile.txt")){ int data = reader.read(); while(data != -) { System.out.print((char) data)); data = reader.read(); } }
请注意,不再有任何显式的close()
方法调用。 try-with-resources构造可以解决这一问题。