BufferedReader之mark与reset初探

先运行一下以下例子:

import java.io.BufferedReader;
import java.io.CharArrayReader;
import java.io.IOException;

class BufferedReaderDemo {
	public static void main(String[] args) throws IOException {
		String s = "test";
		char buf[] = new char[s.length()];
		s.getChars(0, s.length(), buf, 0);
                
		System.out.println(buf);		
		System.out.println(buf.length);	
		                
		CharArrayReader in = new CharArrayReader(buf);
		BufferedReader f = new BufferedReader(in);
		int c, d = 0;
		f.mark(s.length()); 
								
		while ((c = f.read()) != -1) {
			System.out.println(c);
			d++;
		}
		System.out.println("d = " + d);
		f.reset();
	}
}

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

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:
mark in 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方法中:

/**
 * Fills the input buffer, taking the mark into account if it is valid.
 */
private void fill() throws IOException {
    int dst;
    if (markedChar <= UNMARKED) {
        /* No mark */
        dst = 0;
    } else {
        /* Marked */
        int delta = nextChar - markedChar;
        if (delta >= readAheadLimit) {
            /* Gone past read-ahead limit: Invalidate mark */
            markedChar = INVALIDATED;
            readAheadLimit = 0;
            dst = 0;
        } else {
            if (readAheadLimit <= cb.length) {
                /* Shuffle in the current buffer */
                System.arraycopy(cb, markedChar, cb, 0, delta);
                markedChar = 0;
		dst = delta;
            } else {
                /* Reallocate buffer to accommodate read-ahead limit */
                char ncb[] = new char[readAheadLimit];
                System.arraycopy(cb, markedChar, ncb, 0, delta);
                cb = ncb;
                markedChar = 0;
                dst = delta;
            }
            nextChar = nChars = delta;
	}
    }
    int n;
    do {
        n = in.read(cb, dst, cb.length - dst);
    } while (n == 0);
    if (n > 0) {
        nChars = dst + n;
	nextChar = dst;
    }
}

可以看到 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方法中的判断:

if (nextChar >= nChars)
    return -1;

 

 

 

2010.04.27 更正:

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

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

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;

public class Test {
	public static void main(String[] args) throws IOException {
		String s = "This is the internal StringReader buffer.";
		StringReader stringReader = new StringReader(s);
		BufferedReader bufReader = new BufferedReader(stringReader);

		// Read from the underlying StringReader.
		char[] arr = new char[s.length()];
		bufReader.read(arr, 0, arr.length - 14);
		System.out.println(arr);

		// Call mark after having read all but the last 10
		// characters from the StringReader.
		if(bufReader.markSupported()) {
			System.out.println("mark supported.");
			bufReader.mark(3);// change to 15, Mark invalid occurs
		}
		bufReader.read(arr, arr.length - 14, 14);
		bufReader.read();
		
		System.out.println(arr);
		bufReader.reset();

		stringReader.close();
		bufReader.close();
	}
}
 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值