BufferedReader之mark与reset初探

来源:互联网 发布:知柏地黄丸吃多久 编辑:程序博客网 时间:2024/06/05 16:18

先运行一下以下例子:

Java代码  收藏代码
  1. import java.io.BufferedReader;  
  2. import java.io.CharArrayReader;  
  3. import java.io.IOException;  
  4.   
  5. class BufferedReaderDemo {  
  6.     public static void main(String[] args) throws IOException {  
  7.         String s = "test";  
  8.         char buf[] = new char[s.length()];  
  9.         s.getChars(0, s.length(), buf, 0);  
  10.                   
  11.         System.out.println(buf);          
  12.         System.out.println(buf.length);   
  13.                           
  14.         CharArrayReader in = new CharArrayReader(buf);  
  15.         BufferedReader f = new BufferedReader(in);  
  16.         int c, d = 0;  
  17.         f.mark(s.length());   
  18.                                   
  19.         while ((c = f.read()) != -1) {  
  20.             System.out.println(c);  
  21.             d++;  
  22.         }  
  23.         System.out.println("d = " + d);  
  24.         f.reset();  
  25.     }  
  26. }  

 结果是什么呢?出现了异常:

test
4
116
101
115
116
Exception in thread "main" java.io.IOException: Mark invalid 
d = 4
    at java.io.BufferedReader.reset(BufferedReader.java:485)
    at BufferedReaderDemo.main(BufferedReaderDemo.java:24)

乍一想,仅仅f.mark(s.length());非常合理,怎么会出错呢,再试一下,f.mark(s.length() + 1);竟然无错,稍安毋燥,看下javadoc先:

mark

public void mark(int readAheadLimit) throws IOException
Marks the present position in the stream. Subsequent calls to reset() will attempt to reposition the stream to this point.
Overrides:
markin class Reader
Parameters:
readAheadLimit - Limit on the number of characters that may be read while still preserving the mark. An attempt to reset the stream after reading characters up to this limit or beyond may fail. A limit value larger than the size of the input buffer will cause a new buffer to be allocated whose size is no smaller than limit. Therefore large values should be used with care.
Throws:
IllegalArgumentException- If readAheadLimit is < 0
IOException- If an I/O error occurs

关键是参数readAheadLimit的解释,从字面上看是往前读的限制 ,也就是表示“可以再读多少”。再看详细解释:

是指当还保留有此mark时(i.e.mark未变化),可以再读入字符数的限制。当所读字符数达到此限制(即等于该限制)或超过此限制之后尝试重设该流的话(reset the stream),就会导致失败,比方说上例中的异常(产生的原因就是读入的字符数等于readAheadLimit,都是4)。当限制值大于输入缓存(所谓输入缓存,BufferedReader类有两个构造子,其一就有这个参数,无参版本就以默认值替代,大小是8192)时,就会分配一个大小不小于限制值的新缓存(这里说不小于其实就是等于),因此要小心使用大数值。虽然已晓得,但我们再用源码来验证一下上述说法,关键代码在BufferedReader fill方法中:

Java代码  收藏代码
  1. /** 
  2.  * Fills the input buffer, taking the mark into account if it is valid. 
  3.  */  
  4. private void fill() throws IOException {  
  5.     int dst;  
  6.     if (markedChar <= UNMARKED) {  
  7.         /* No mark */  
  8.         dst = 0;  
  9.     } else {  
  10.         /* Marked */  
  11.         int delta = nextChar - markedChar;  
  12.         if (delta >= readAheadLimit) {  
  13.             /* Gone past read-ahead limit: Invalidate mark */  
  14.             markedChar = INVALIDATED;  
  15.             readAheadLimit = 0;  
  16.             dst = 0;  
  17.         } else {  
  18.             if (readAheadLimit <= cb.length) {  
  19.                 /* Shuffle in the current buffer */  
  20.                 System.arraycopy(cb, markedChar, cb, 0, delta);  
  21.                 markedChar = 0;  
  22.         dst = delta;  
  23.             } else {  
  24.                 /* Reallocate buffer to accommodate read-ahead limit */  
  25.                 char ncb[] = new char[readAheadLimit];  
  26.                 System.arraycopy(cb, markedChar, ncb, 0, delta);  
  27.                 cb = ncb;  
  28.                 markedChar = 0;  
  29.                 dst = delta;  
  30.             }  
  31.             nextChar = nChars = delta;  
  32.     }  
  33.     }  
  34.     int n;  
  35.     do {  
  36.         n = in.read(cb, dst, cb.length - dst);  
  37.     } while (n == 0);  
  38.     if (n > 0) {  
  39.         nChars = dst + n;  
  40.     nextChar = dst;  
  41.     }  
  42. }  

可以看到 nextChar - markedChar >= readAheadLimit 就会被视为Gone past read-ahead limit: Invalidate mark

这里的nextChar是int型,指示当前读入指针下一字符的位置,比如未读前就是0,读了一个字符之后就是1,还有看当非readAheadLimit <= cb.length时,也即readAheadLimit > cb.length,就分配新缓存了,缓存大小就是readAheadLimit,而nextChar在此fill操作中是不变的,所以仍是dst,即delta。最后的读n值主要是为了给nChars赋值,nChars含义应该是总共流中有多少字符,这样也就有了read方法中的判断:

Java代码  收藏代码
  1. if (nextChar >= nChars)  
  2.     return -1;  

 

 

 

2010.04.27 更正: 

原帖中有bug:不是所读超过readAheadLimit就会报异常,而是还有个更大的前提条件,就是所读字符已越过流尾时。

综述:当所读字符已越过流尾时,才可能再判断所读字符数是否大于等于readAheadLimit。

Java代码  收藏代码
  1. import java.io.BufferedReader;  
  2. import java.io.IOException;  
  3. import java.io.StringReader;  
  4.   
  5. public class Test {  
  6.     public static void main(String[] args) throws IOException {  
  7.         String s = "This is the internal StringReader buffer.";  
  8.         StringReader stringReader = new StringReader(s);  
  9.         BufferedReader bufReader = new BufferedReader(stringReader);  
  10.   
  11.         // Read from the underlying StringReader.  
  12.         char[] arr = new char[s.length()];  
  13.         bufReader.read(arr, 0, arr.length - 14);  
  14.         System.out.println(arr);  
  15.   
  16.         // Call mark after having read all but the last 10  
  17.         // characters from the StringReader.  
  18.         if(bufReader.markSupported()) {  
  19.             System.out.println("mark supported.");  
  20.             bufReader.mark(3);// change to 15, Mark invalid occurs  
  21.         }  
  22.         bufReader.read(arr, arr.length - 1414);  
  23.         bufReader.read();  
  24.           
  25.         System.out.println(arr);  
  26.         bufReader.reset();  
  27.   
  28.         stringReader.close();  
  29.         bufReader.close();  
  30.     }  
  31. }