Java 中的 BufferedReader 介绍_动力节点Java学院整理
BufferedReader介绍
BufferedReader是缓冲字符输入流。它继承于Reader。
BufferedReader的作用是为其他字符输入流添加一些缓冲功能。
BufferedReader函数列表
BufferedReader(Readerin) BufferedReader(Readerin,intsize) voidclose() voidmark(intmarkLimit) booleanmarkSupported() intread() intread(char[]buffer,intoffset,intlength) StringreadLine() booleanready() voidreset() longskip(longcharCount)
BufferedReader源码分析(基于jdk1.7.40)
packagejava.io;
publicclassBufferedReaderextendsReader{
privateReaderin;
//字符缓冲区
privatecharcb[];
//nChars是cb缓冲区中字符的总的个数
//nextChar是下一个要读取的字符在cb缓冲区中的位置
privateintnChars,nextChar;
//表示“标记无效”。它与UNMARKED的区别是:
//(01)UNMARKED是压根就没有设置过标记。
//(02)而INVALIDATED是设置了标记,但是被标记位置太长,导致标记无效!
privatestaticfinalintINVALIDATED=-2;
//表示没有设置“标记”
privatestaticfinalintUNMARKED=-1;
//“标记”
privateintmarkedChar=UNMARKED;
//“标记”能标记位置的最大长度
privateintreadAheadLimit=0;/*ValidonlywhenmarkedChar>0*/
//skipLF(即skipLineFeed)是“是否忽略换行符”标记
privatebooleanskipLF=false;
//设置“标记”时,保存的skipLF的值
privatebooleanmarkedSkipLF=false;
//默认字符缓冲区大小
privatestaticintdefaultCharBufferSize=8192;
//默认每一行的字符个数
privatestaticintdefaultExpectedLineLength=80;
//创建“Reader”对应的BufferedReader对象,sz是BufferedReader的缓冲区大小
publicBufferedReader(Readerin,intsz){
super(in);
if(sz<=0)
thrownewIllegalArgumentException("Buffersize<=0");
this.in=in;
cb=newchar[sz];
nextChar=nChars=0;
}
//创建“Reader”对应的BufferedReader对象,默认的BufferedReader缓冲区大小是8k
publicBufferedReader(Readerin){
this(in,defaultCharBufferSize);
}
//确保“BufferedReader”是打开状态
privatevoidensureOpen()throwsIOException{
if(in==null)
thrownewIOException("Streamclosed");
}
//填充缓冲区函数。有以下两种情况被调用:
//(01)缓冲区没有数据时,通过fill()可以向缓冲区填充数据。
//(02)缓冲区数据被读完,需更新时,通过fill()可以更新缓冲区的数据。
privatevoidfill()throwsIOException{
//dst表示“cb中填充数据的起始位置”。
intdst;
if(markedChar<=UNMARKED){
//没有标记的情况,则设dst=0。
dst=0;
}else{
//delta表示“当前标记的长度”,它等于“下一个被读取字符的位置”减去“标记的位置”的差值;
intdelta=nextChar-markedChar;
if(delta>=readAheadLimit){
//若“当前标记的长度”超过了“标记上限(readAheadLimit)”,
//则丢弃标记!
markedChar=INVALIDATED;
readAheadLimit=0;
dst=0;
}else{
if(readAheadLimit<=cb.length){
//若“当前标记的长度”没有超过了“标记上限(readAheadLimit)”,
//并且“标记上限(readAheadLimit)”小于/等于“缓冲的长度”;
//则先将“下一个要被读取的位置,距离我们标记的置符的距离”间的字符保存到cb中。
System.arraycopy(cb,markedChar,cb,0,delta);
markedChar=0;
dst=delta;
}else{
//若“当前标记的长度”没有超过了“标记上限(readAheadLimit)”,
//并且“标记上限(readAheadLimit)”大于“缓冲的长度”;
//则重新设置缓冲区大小,并将“下一个要被读取的位置,距离我们标记的置符的距离”间的字符保存到cb中。
charncb[]=newchar[readAheadLimit];
System.arraycopy(cb,markedChar,ncb,0,delta);
cb=ncb;
markedChar=0;
dst=delta;
}
//更新nextChar和nChars
nextChar=nChars=delta;
}
}
intn;
do{
//从“in”中读取数据,并存储到字符数组cb中;
//从cb的dst位置开始存储,读取的字符个数是cb.length-dst
//n是实际读取的字符个数;若n==0(即一个也没读到),则继续读取!
n=in.read(cb,dst,cb.length-dst);
}while(n==0);
//如果从“in”中读到了数据,则设置nChars(cb中字符的数目)=dst+n,
//并且nextChar(下一个被读取的字符的位置)=dst。
if(n>0){
nChars=dst+n;
nextChar=dst;
}
}
//从BufferedReader中读取一个字符,该字符以int的方式返回
publicintread()throwsIOException{
synchronized(lock){
ensureOpen();
for(;;){
//若“缓冲区的数据已经被读完”,
//则先通过fill()更新缓冲区数据
if(nextChar>=nChars){
fill();
if(nextChar>=nChars)
return-1;
}
//若要“忽略换行符”,
//则对下一个字符是否是换行符进行处理。
if(skipLF){
skipLF=false;
if(cb[nextChar]=='\n'){
nextChar++;
continue;
}
}
//返回下一个字符
returncb[nextChar++];
}
}
}
//将缓冲区中的数据写入到数组cbuf中。off是数组cbuf中的写入起始位置,len是写入长度
privateintread(char[]cbuf,intoff,intlen)throwsIOException{
//若“缓冲区的数据已经被读完”,则更新缓冲区数据。
if(nextChar>=nChars){
if(len>=cb.length&&markedChar<=UNMARKED&&!skipLF){
returnin.read(cbuf,off,len);
}
fill();
}
//若更新数据之后,没有任何变化;则退出。
if(nextChar>=nChars)return-;
//若要“忽略换行符”,则进行相应处理
if(skipLF){
skipLF=false;
if(cb[nextChar]=='\n'){
nextChar++;
if(nextChar>=nChars)
fill();
if(nextChar>=nChars)
return-1;
}
}
//拷贝字符操作
intn=Math.min(len,nChars-nextChar);
System.arraycopy(cb,nextChar,cbuf,off,n);
nextChar+=n;
returnn;
}
//对read()的封装,添加了“同步处理”和“阻塞式读取”等功能
publicintread(charcbuf[],intoff,intlen)throwsIOException{
synchronized(lock){
ensureOpen();
if((off<0)||(off>cbuf.length)||(len<0)||
((off+len)>cbuf.length)||((off+len)<0)){
thrownewIndexOutOfBoundsException();
}elseif(len==0){
return0;
}
intn=read1(cbuf,off,len);
if(n<=0)returnn;
while((n=nChars)
fill();
if(nextChar>=nChars){/*EOF*/
if(s!=null&&s.length()>0)
returns.toString();
else
returnnull;
}
booleaneol=false;
charc=0;
inti;
/*Skipaleftover'\n',ifnecessary*/
if(omitLF&&(cb[nextChar]=='\n'))
nextChar++;
skipLF=false;
omitLF=false;
charLoop:
for(i=nextChar;i0){
if(nextChar>=nChars)
fill();
if(nextChar>=nChars)/*EOF*/
break;
if(skipLF){
skipLF=false;
if(cb[nextChar]=='\n'){
nextChar++;
}
}
longd=nChars-nextChar;
if(r<=d){
nextChar+=r;
r=0;
break;
}
else{
r-=d;
nextChar=nChars;
}
}
returnn-r;
}
}
//“下一个字符”是否可读
publicbooleanready()throwsIOException{
synchronized(lock){
ensureOpen();
//若忽略换行符为true;
//则判断下一个符号是否是换行符,若是的话,则忽略
if(skipLF){
if(nextChar>=nChars&&in.ready()){
fill();
}
if(nextChar
说明:
要想读懂BufferReader的源码,就要先理解它的思想。BufferReader的作用是为其它Reader提供缓冲功能。创建BufferReader时,我们会通过它的构造函数指定某个Reader为参数。BufferReader会将该Reader中的数据分批读取,每次读取一部分到缓冲中;操作完缓冲中的这部分数据之后,再从Reader中读取下一部分的数据。
为什么需要缓冲呢?原因很简单,效率问题!缓冲中的数据实际上是保存在内存中,而原始数据可能是保存在硬盘或NandFlash中;而我们知道,从内存中读取数据的速度比从硬盘读取数据的速度至少快10倍以上。
那干嘛不干脆一次性将全部数据都读取到缓冲中呢?第一,读取全部的数据所需要的时间可能会很长。第二,内存价格很贵,容量不想硬盘那么大。
下面,我就BufferReader中最重要的函数fill()进行说明。其它的函数很容易理解,我就不详细介绍了,大家可以参考源码中的注释进行理解。我们先看看fill()的源码:
privatevoidfill()throwsIOException{
intdst;
if(markedChar<=UNMARKED){
/*Nomark*/
dst=0;
}else{
/*Marked*/
intdelta=nextChar-markedChar;
if(delta>=readAheadLimit){
/*Gonepastread-aheadlimit:Invalidatemark*/
markedChar=INVALIDATED;
readAheadLimit=0;
dst=0;
}else{
if(readAheadLimit<=cb.length){
/*Shuffleinthecurrentbuffer*/
System.arraycopy(cb,markedChar,cb,0,delta);
markedChar=0;
dst=delta;
}else{
/*Reallocatebuffertoaccommodateread-aheadlimit*/
charncb[]=newchar[readAheadLimit];
System.arraycopy(cb,markedChar,ncb,0,delta);
cb=ncb;
markedChar=0;
dst=delta;
}
nextChar=nChars=delta;
}
}
intn;
do{
n=in.read(cb,dst,cb.length-dst);
}while(n==0);
if(n>0){
nChars=dst+n;
nextChar=dst;
}
}
根据fill()中的if...else...,我将fill()分为4种情况进行说明。
情况1:读取完缓冲区的数据,并且缓冲区没有被标记
执行流程如下,
(01)其它函数调用fill(),来更新缓冲区的数据
(02)fill()执行代码if(markedChar<=UNMARKED){...}
为了方便分析,我们将这种情况下fill()执行的操作等价于以下代码:
privatevoidfill()throwsIOException{
intdst;
if(markedChar<=UNMARKED){
/*Nomark*/
dst=0;
}
intn;
do{
n=in.read(cb,dst,cb.length-dst);
}while(n==0);
if(n>0){
nChars=dst+n;
nextChar=dst;
}
}
说明:
这种情况发生的情况是——Reader中有很长的数据,我们每次从中读取一部分数据到缓冲中进行操作。每次当我们读取完缓冲中的数据之后,并且此时BufferedReader没有被标记;那么,就接着从Reader(BufferReader提供缓冲功能的Reader)中读取下一部分的数据到缓冲中。
其中,判断是否读完缓冲区中的数据,是通过“比较nextChar和nChars之间大小”来判断的。其中,nChars是缓冲区中字符的总的个数,而nextChar是缓冲区中下一个要读取的字符的位置。
判断BufferedReader有没有被标记,是通过“markedChar”来判断的。
理解这个思想之后,我们再对这种情况下的fill()的代码进行分析,就特别容易理解了。
(01)if(markedChar<=UNMARKED)它的作用是判断“BufferedReader是否被标记”。若被标记,则dst=0。
(02)in.read(cb,dst,cb.length-dst)等价于in.read(cb,0,cb.length),意思是从Reader对象in中读取cb.length个数据,并存储到缓冲区cb中,而且从缓冲区cb的位置0开始存储。该函数返回值等于n,也就是n表示实际读取的字符个数。若n=0(即没有读取到数据),则继续读取,直到读到数据为止。
(03)nChars=dst+n等价于nChars=n;意味着,更新缓冲区数据cb之后,设置nChars(缓冲区的数据个数)为n。
(04)nextChar=dst等价于nextChar=0;意味着,更新缓冲区数据cb之后,设置nextChar(缓冲区中下一个会被读取的字符的索引值)为0。
情况2:读取完缓冲区的数据,缓冲区的标记位置>0,并且“当前标记的长度”超过“标记上限(readAheadLimit)”
执行流程如下,
(01)其它函数调用fill(),来更新缓冲区的数据
(02)fill()执行代码if(delta>=readAheadLimit){...}
为了方便分析,我们将这种情况下fill()执行的操作等价于以下代码:
privatevoidfill()throwsIOException{
intdst;
if(markedChar>UNMARKED){
intdelta=nextChar-markedChar;
if(delta>=readAheadLimit){
markedChar=INVALIDATED;
readAheadLimit=0;
dst=0;
}
}
intn;
do{
n=in.read(cb,dst,cb.length-dst);
}while(n==0);
if(n>0){
nChars=dst+n;
nextChar=dst;
}
}
说明:
这种情况发生的情况是——BufferedReader中有很长的数据,我们每次从中读取一部分数据到缓冲区中进行操作。当我们读取完缓冲区中的数据之后,并且此时,BufferedReader存在标记时,同时,“当前标记的长度”大于“标记上限”;那么,就发生情况2。此时,我们会丢弃“标记”并更新缓冲区。
(01)delta=nextChar-markedChar;其中,delta就是“当前标记的长度”,它是“下一个被读取字符的位置”减去“被标记的位置”的差值。
(02)if(delta>=readAheadLimit);其中,当delta>=readAheadLimit,就意味着,“当前标记的长度”>=“标记上限”。为什么要有标记上限,即readAheadLimit的值到底有何意义呢?
我们标记一个位置之后,更新缓冲区的时候,被标记的位置会被保存;当我们不停的更新缓冲区的时候,被标记的位置会被不停的放大。然后内存的容量是有效的,我们不可能不限制长度的存储标记。所以,需要readAheadLimit来限制标记长度!
(03)in.read(cb,dst,cb.length-dst)等价于in.read(cb,0,cb.length),意思是从Reader对象in中读取cb.length个数据,并存储到缓冲区cb中,而且从缓冲区cb的位置0开始存储。该函数返回值等于n,也就是n表示实际读取的字符个数。若n=0(即没有读取到数据),则继续读取,直到读到数据为止。
(04)nChars=dst+n等价于nChars=n;意味着,更新缓冲区数据cb之后,设置nChars(缓冲区的数据个数)为n。
(05)nextChar=dst等价于nextChar=0;意味着,更新缓冲区数据cb之后,设置nextChar(缓冲区中下一个会被读取的字符的索引值)为0。
情况3:读取完缓冲区的数据,缓冲区的标记位置>0,“当前标记的长度”没超过“标记上限(readAheadLimit)”,并且“标记上限(readAheadLimit)”小于/等于“缓冲的长度”;
执行流程如下,
(01)其它函数调用fill(),来更新缓冲区的数据
(02)fill()执行代码if(readAheadLimit<=cb.length){...}
为了方便分析,我们将这种情况下fill()执行的操作等价于以下代码:
privatevoidfill()throwsIOException{
intdst;
if(markedChar>UNMARKED){
intdelta=nextChar-markedChar;
if((delta0){
nChars=dst+n;
nextChar=dst;
}
}
说明:
这种情况发生的情况是——BufferedReader中有很长的数据,我们每次从中读取一部分数据到缓冲区中进行操作。当我们读取完缓冲区中的数据之后,并且此时,BufferedReader存在标记时,同时,“当前标记的长度”小于“标记上限”,并且“标记上限”小于/等于“缓冲区长度”;那么,就发生情况3。此时,我们保留“被标记的位置”(即,保留被标记位置开始的数据),并更新缓冲区(将新增的数据,追加到保留的数据之后)。
情况4:读取完缓冲区的数据,缓冲区的标记位置>0,“当前标记的长度”没超过“标记上限(readAheadLimit)”,并且“标记上限(readAheadLimit)”大于“缓冲的长度”;
执行流程如下,
(01)其它函数调用fill(),来更新缓冲区的数据
(02)fill()执行代码else{charncb[]=newchar[readAheadLimit];...}
为了方便分析,我们将这种情况下fill()执行的操作等价于以下代码:
privatevoidfill()throwsIOException{
intdst;
if(markedChar>UNMARKED){
intdelta=nextChar-markedChar;
if((deltacb.length)){
charncb[]=newchar[readAheadLimit];
System.arraycopy(cb,markedChar,ncb,0,delta);
cb=ncb;
markedChar=0;
dst=delta;
nextChar=nChars=delta;
}
}
intn;
do{
n=in.read(cb,dst,cb.length-dst);
}while(n==);
if(n>){
nChars=dst+n;
nextChar=dst;
}
}
说明:
这种情况发生的情况是——BufferedReader中有很长的数据,我们每次从中读取一部分数据到缓冲区中进行操作。当我们读取完缓冲区中的数据之后,并且此时,BufferedReader存在标记时,同时,“当前标记的长度”小于“标记上限”,并且“标记上限”大于“缓冲区长度”;那么,就发生情况4。此时,我们要先更新缓冲区的大小,然后再保留“被标记的位置”(即,保留被标记位置开始的数据),并更新缓冲区数据(将新增的数据,追加到保留的数据之后)。
示例代码
关于BufferedReader中API的详细用法,参考示例代码(BufferedReaderTest.java):
importjava.io.BufferedReader;
importjava.io.ByteArrayInputStream;
importjava.io.File;
importjava.io.InputStream;
importjava.io.FileReader;
importjava.io.IOException;
importjava.io.FileNotFoundException;
importjava.lang.SecurityException;
/**
*BufferedReader测试程序
*
*
*/
publicclassBufferedReaderTest{
privatestaticfinalintLEN=5;
publicstaticvoidmain(String[]args){
testBufferedReader();
}
/**
*BufferedReader的API测试函数
*/
privatestaticvoidtestBufferedReader(){
//创建BufferedReader字符流,内容是ArrayLetters数组
try{
Filefile=newFile("bufferedreader.txt");
BufferedReaderin=
newBufferedReader(
newFileReader(file));
//从字符流中读取5个字符。“abcde”
for(inti=0;i
程序中读取的bufferedreader.txt的内容如下:
abcdefghijklmnopqrstuvwxyz
0123456789
ABCDEFGHIJKLMNOPQRSTUVWXYZ
运行结果:
0:a
1:b
2:c
3:d
4:e
buf=01234
readLine=56789
buf=fghij
以上所述是小编给大家介绍的Java中的BufferedReader介绍_动力节点Java学院整理,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!