Java BufferedReader
Java BufferedReader类java.io.BufferedReader为Java Reader实例提供缓冲。缓冲可以大大提高IO的速度。 JavaBufferedReader
不是一次从底层的Reader中读取一个字符,而是一次读取一个更大的块(数组)。这通常要快得多,尤其是对于磁盘访问和更大的数据量而言。
Java的BufferedReader与BufferedInputStream相似,但是它们并不完全相同。 BufferedReader和BufferedInputStream之间的主要区别是BufferedReader读取字符(文本),而BufferedInputStream读取原始字节。
JavaBufferedReader
类是Java Reader类的子类,因此我们可以在需要Reader
的任何地方使用BufferedReader
。
Java BufferedReader示例
要将缓冲添加到JavaReader
实例,只需将其包装在BufferedReader
中即可。看起来是这样的:
BufferedReader bufferedReader = new BufferedReader( new FileReader("c:\data\input-file.txt"));
这个例子创建了一个" BufferedReader",它包装了一个" FileReader"。 BufferedReader
将从FileReader
中读取一个字符块(通常读入char
数组中)。因此,从read()返回的每个字符都从此内部数组返回。完全读取数组后," BufferedReader"会将新的数据块读入数组等。
BufferedReader缓冲区大小
我们可以将缓冲区大小设置为由BufferedReader内部使用。我们将size作为构造函数参数提供,如下所示:
int bufferSize = 8 * 1024; BufferedReader bufferedReader = new BufferedReader( new FileReader("c:\data\input-file.txt"), bufferSize );
本示例将内部缓冲区设置为8 KB。最好使用1024个字节的倍数的缓冲区大小。这与硬盘等大多数内置缓冲一起使用时效果最佳。
除了向Reader
实例添加缓冲外,JavaBufferedReader
的行为几乎类似于Reader
。不过,BufferedReader
还有一个额外的方法,readLine()
方法。如果我们需要一次读取输入一行,则此方法很方便。这是一个BufferedReader``readLine()
的例子:
String line = bufferedReader.readLine();
readLine()方法将返回从BufferedReader中读取的文本行(找到换行符之前的所有文本)。如果没有更多的数据可以从底层的Reader中读取,则BufferedReader的readLine()方法将返回null。
从BufferedReader读取字符
JavaBufferedReader
的read()
方法返回一个int值,其中包含下一个读取的字符的char
值。如果read()
方法返回-1,则说明BufferedReader
中没有更多的数据可读取,因此可以将其关闭。也就是说,-1作为int值,而不是-1作为byte或者char值。这里有区别!
这是一个从JavaBufferedReader
中读取所有字符的示例:
Reader reader = new BufferedReader( 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(); }
请注意,代码示例如何首先从JavaBufferedReader
中读取单个字符并检查char数值是否等于-1. 如果没有,它将处理该char并继续读取,直到从BufferedReader的read()方法返回-1为止。
如前所述," BufferedReader"实际上将从底层的" Reader"中读取一个字符数组,然后逐个返回这些字符,而不是将每个" read()"调用都转发给底层的" Reader"。读取完内部缓冲区中的所有字符后," BufferedReader"会尝试再次填充缓冲区,直到无法从底层的" Reader"中读取更多字符为止。
从BufferedReader读取字符数组
Java的" BufferedReader"类还具有一个" read()"方法,该方法采用" char"数组作为参数,以及起始偏移量和长度。 char数组是read()方法将字符读入的数组。 offset参数是在char数组中的read()方法应该开始读入的位置。 length参数是read()方法应从偏移量开始并向前读取到char数组中的字符数。以下是使用JavaBufferedReader
将字符数组读入char
数组的示例:
Reader reader = new BufferedReader( 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数组的字符数,如果BufferedReader中没有更多的字符可读取,则返回-1,例如已达到" BufferedReader"所连接的文件。
从BufferedReader读取一行
Java的BufferedReader具有一个名为readLine()的特殊读取方法,该方法从BufferedReader的内部缓冲区中读取整行文本。 readLine()方法返回一个String。如果没有更多的行要从" BufferedReader"读取,则" readLine()"方法将返回" null"。这是一个使用JavaBufferedReader
逐行读取文本文件行的示例:
BufferedReader bufferedReader = new BufferedReader( new FileReader("/path/to/file/thefile.txt")); String line = bufferedReader.readLine(); while(line != null) { System.out.println(line); line = bufferedReader.readLine(); }
读取的性能
一次读取一个字符数组比一次从JavaReader
读取单个字符要快。但是,由于" BufferedReader"已经进行了一些内部缓冲,因此这种差异很可能不像不使用缓冲的" Reader"那样明显。不过,我们很可能仍会看到很小的差异。
跳过字符
JavaBufferedReader
类有一个名为skip()
的方法,可用于跳过我们不想读取的输入中的许多字符。我们将要跳过的字符数作为参数传递给skip()
方法。这是一个从JavaBufferedReader
跳过字符的示例:
long charsSkipped = bufferedReader.skip(24);
这个例子告诉JavaBufferedReader
跳过BufferedReader
中接下来的24个字符。 skip()
方法返回被跳过的实际字符数。在大多数情况下,该数字与我们请求的跳过数字相同,但是如果BufferedReader中剩余的字符少于我们请求跳过的数字,则返回的跳过字符数可以小于我们请求的字符数跳过了。
关闭BufferedReader
当我们从BufferedReader
中读取完字符后,我们应该记住将其关闭。关闭" BufferedReader"也会关闭" BufferedReader"正在读取的" Reader"实例。
关闭" BufferedReader"是通过调用其" close()"方法完成的。这是关闭BufferedReader
的样子:
bufferedReader.close();
我们还可以使用Java 7中引入的try-with-resources构造。这是如何使用try-with-resources构造使用和关闭" BufferedReader"外观的方法:
Reader reader = new FileReader("data/data.bin"); try(BufferedReader bufferedReader = new BufferedReader(reader)){ String line = bufferedReader.readLine(); while(line != null) { //do something with line line = bufferedReader.readLine(); } }
请注意,不再有任何显式的close()
方法调用。 try-with-resources构造可以解决这一问题。
还要注意,第一个FileReader
实例不是在try-with-resources块内创建的。这意味着try-with-resources块不会自动关闭此FileReader实例。但是,当关闭BufferedReader时,它还将关闭从中读取的Reader实例,因此当BufferedReader关闭时,FileReader实例也将关闭。
可重用的BufferedReader
标准Java" BufferedReader"的缺点之一是只能使用一次。一旦关闭它,它就不再可用。如果需要读取大量文件或者网络流,则必须为要读取的每个文件或者网络流创建一个新的BufferedReader。这意味着我们要创建一个新对象,更重要的是,要创建一个新的char数组,该数组用作BufferedReader中的缓冲区。如果读取的文件或者流的数量很高,并且彼此之间读取很快,这可能会对Java垃圾收集器造成压力。
一种替代方法是创建一个可重用的BufferedReader,其中我们可以替换基础源Reader
,以便可以重用BufferedReader及其内部字节数组缓冲区。为了节省麻烦,我创建了这样一个ReusableBufferedReader
,并在本教程的后续部分中包含了该代码。首先,我想向我们展示如何使用此" ReusableBufferedReader"外观。
创建一个ReusableBufferedReader
首先,我们需要创建一个" ReusableBufferedReader"。这是一个如何创建ReusableBufferedReader
的例子:
ReusableBufferedReader reusableBufferedReader = new ReusableBufferedReader(new char[1024 * 1024]);
本示例创建一个具有2 MB字符数组(1024 \ * 1024个字符,1个字符= 2个字节)的" ReusableBufferedReader"作为内部缓冲区。
设定来源
当创建了一个" ReusableBufferedReader"时,我们需要在其上设置" Reader"以用作基础数据源。这是在ReusableBufferedReader
上设置源Reader
的方法:
FileReader reader = new FileReader("/mydata/somefile.txt"); reusableBufferedReader.setSource(reader);
setSource()方法实际上返回对ReusableBufferedReader的引用,因此我们实际上可以创建一个ReusableBufferedReader并在一条指令中设置源:
ReusableBufferedReader reusableBufferedReader = new ReusableBufferedReader(new byte[1024 * 1024]) .setSource(new FileReader("/mydata/somefile.txt"));
重用一个ReusableBufferedReader
使用完ReusableBufferedReader
之后,我们需要关闭它。关闭它只会关闭底层的源Reader
。关闭ReusableBufferedReader
之后,我们可以再次使用它,只需在其上设置一个新的源Reader
。重用ReusableBufferedReader
的样子如下:
reusableBufferedReader.setSource(new FileReader("/mydata/file-1.txt")); //read data from ReusableBufferedReader reusableBufferedReader.close(); reusableBufferedReader.setSource(new FileReader("/mydata/file-1.txt")); //read data from ReusableBufferedReader reusableBufferedReader.close();
ReusableBufferedReader代码
这是上述ReusableBufferedReader
的代码。注意,该实现仅覆盖其扩展的Reader类的read()和read(char [] dest,int offset,int length)方法。其余的Reader
方法已被省略,以使代码更短,但是我们可以自己实现它们,以备需要时使用。
import java.io.IOException; import java.io.Reader; public class ReusableBufferedReader extends Reader { private char[] buffer = null; private int writeIndex = 0; private int readIndex = 0; private boolean endOfReaderReached = false; private Reader source = null; public ReusableBufferedReader(char[] buffer) { this.buffer = buffer; } public ReusableBufferedReader setSource(Reader source){ this.source = source; this.writeIndex = 0; this.readIndex = 0; this.endOfReaderReached = false; return this; } @Override public int read() throws IOException { if(endOfReaderReached) { return -1; } if(readIndex == writeIndex) { if(writeIndex == buffer.length) { this.writeIndex = 0; this.readIndex = 0; } //data should be read into buffer. int bytesRead = readCharsIntoBuffer(); while(bytesRead == 0) { //continue until you actually get some bytes ! bytesRead = readCharsIntoBuffer(); } //if no more data could be read in, return -1; if(bytesRead == -1) { return -1; } } return 65535 & this.buffer[readIndex++]; } @Override public int read(char[] dest, int offset, int length) throws IOException { int charsRead = 0; int data = 0; while(data != -1 && charsRead < length){ data = read()%3b.html if(data == -1) { endOfReaderReached = true; if(charsRead == 0){ return -1; } return charsRead; } dest[offset + charsRead] = (char) (65535 & data); charsRead++; } return charsRead; } private int readCharsIntoBuffer() throws IOException { int charsRead = this.source.read(this.buffer, this.writeIndex, this.buffer.length - this.writeIndex); writeIndex += charsRead; return charsRead; } @Override public void close() throws IOException { this.source.close(); } }