Java BufferedReader

时间:2020-01-09 10:36:01  来源:igfitidea点击:

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读取字符

JavaBufferedReaderread()方法返回一个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();
    }
}