字符缓冲流
类关系
结合一句代码了解这个类图
BufferedReader bufferedReader = new BufferedReader(new FileReader(inputFile))
new了一个BufferedReader。输入是一个FileReader。
也就是BufferedReader里面的in变量就是FileReader。
另外几个关联的属性:cb,nchars,nextchar等
看看cb的定义:char cb[]
,BufferedReader是缓冲字符输入流。cb就是字符缓冲区,默认8192长度。作用是存储一次和操作系统io交互之后,读取到的8192个字符数据。
而nchars的含义是当前cb中的字符总数,nextchar是下一个读取的数据的数组下标。
缓冲区读数据算法
当调用bufferedReader.read()
时候看下是怎么工作的
缓冲区字符数组取数据的简单算法描述:
1、当第一次读取一个字符的时候,nextchar =0; nchars=0;这是什么含义,这就是说cb字符数组中没有数据,那就调用FileReader读取一组字符数组到cb中。同时修改nchars=8192;返回cb[extchar++]
2、当读取第二个字符的时候,nextchar=1,nchars=8192。那直接返回cb[nextchar];
3、因为nextchar是从0开始的,当读取完字符缓冲去8192个字符后,下一次读取的时候nextchar=8192。这个时候发现nextchar大于等于nchars=8192;就说明已经读取完缓冲区所有数据了。要重新调用FileReader从操作系统文件中读取下一组数据放到字符缓冲区cb中,并且将nextchar设置为0,nchars=8192;
4、不断往复
FileReader.read()
看上面类图FileReader是InputStreamReader的子类,InputStreamReader有属性sd,就是StreamDecoder。
FileReader.read()调用的是InputStreamReader的read()方法,如下,可以看到实际上调用的是StreamDecoder的read方法。
public int read(char cbuf[], int offset, int length) throws IOException {
return sd.read(cbuf, offset, length);
}
StreamDecoder原理
StreamDecoder向上,对FileReader提供了读取字符到字符数组的能力。
向下,面向操作系统文件,通过字节流读取字节数据到ByteBuffer类型的bb属性中。
考虑一个事情:
当我们通过ReadableByteChannel ch
读取一次数据,这时候,放入bb缓冲区,bb的大小是8192。
而StreamDecoder面向FileReader提供读取字符的能力,假如一次读取8192个字符到字符数组中。
这个时候,因为ByteBuffer bb中有8192个字节。那是不是8192个字节就能转换为8192个字符返回给FileReader呢?
答案是否定的。
字符包括中英文特殊符号等。比如一个中文UTF_8编码,通常使用3或者4个字节编码。
所以bb中的8192个字节,可能转换成3000+个字符。
这个时候bb缓冲区的数据已经解码完了。
需要继续通过ReadableByteChannel ch继续读取字节数据到bb字节缓冲区。
StreamDecoder读取字符细节
看下StreamDecoder读取内容的方法
int implRead(char[] cbuf, int off, int end) throws IOException
...
CoderResult cr = decoder.decode(bb, cb, eof);
if (cr.isUnderflow()) {
if (eof)
break;
if (!cb.hasRemaining())
break;
if ((cb.position() > 0) && !inReady())
break; // Block at most once
int n = readBytes();
if (n < 0) {
eof = true;
if ((cb.position() == 0) && (!bb.hasRemaining()))
break;
decoder.reset();
}
continue;
}
if (cr.isOverflow()) {
assert cb.position() > 0;
break;
}
...
其中decoder是指定或者系统默认的编解码decoder,比如UTF_8
StreamDecoder使用了具体的编解码器对bb的bytebuffer字节数组的字节进行字符编码,并且将编码后的字符写入cb中。
解码CoderResult有两种结果要重点说下:
1、CoderResult的type是CR_UNDERFLOW
,代表bb字节中已经读取完了但是还没填充到cb字符数组需要读取的长度,这个时候要继续从FileInputStream中读取8192个字节存储到bb中,进行继续解码
2、CoderResult的type是CR_OVERFLOW
,代表解码器decoder解码字节缓冲数组的bb的内容,并切存储到cb字符数组过程中,cb字符数组已经满了,但是字节数组还没有解码完。这个时候记录一下bb字节缓冲数组的,以供下次继续解码。