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 116Exception 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クラスには2つの構造子があり、1つにこのパラメータがあり、パラメータなしバージョンでデフォルト値で置き換えられ、サイズは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文字を読んだ後は1、readAheadLimit<=cbではない.lengthの場合、すなわちreadAheadLimit>cb.lengthでは、新しいキャッシュが割り当てられ、キャッシュサイズはreadAheadLimitですが、nextCharはこのfill操作では変わらないので、dst、すなわちdeltaです.最後のn値の読み取りは主にnCharsに値を割り当てるためであり、nCharsの意味は合計ストリームにどれだけの文字があるべきかであり、readメソッドでの判断もある.
if (nextChar >= nChars)
    return -1;

 
 
 
2010.04.27訂正:
元帖にはバグがあります.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();
	}
}