JAVA IO 의 Buffered Reader 소스 코드 분석

전재 하 다http://www.fengfly.com/plus/view-214073-1.html
**
BufferedReader 소개
* * Buffered Reader 는 버퍼 문자 입력 흐름 입 니 다.그것 은 Reader 에 계승 되 었 다.Buffered Reader 의 역할 은 다른 문자 의 입력 흐름 에 버퍼 기능 을 추가 하 는 것 입 니 다.BufferedReader 함수 목록
BufferedReader(Reader in)  
BufferedReader(Reader in, int size)  
 
void     close()  
void     mark(int markLimit)  
boolean  markSupported()  
int      read()  
int      read(char[] buffer, int offset, int length)  
String   readLine()  
boolean  ready()  
void     reset()  
long     skip(long charCount) 

**
BufferedReader 소스 코드 분석
**
public class BufferedReader extends Reader {
       
 
    private Reader in;  
 
    //        
    private char cb[];  
    // nChars  cb             
    // nextChar            cb         
    private int nChars, nextChar;  
 
    //   “    ”。  UNMARKED    :  
    // (01) UNMARKED            。  
    // (02)  INVALIDATED      ,         ,      !  
    private static final int INVALIDATED = -2;  
    //       “  ”  
    private static final int UNMARKED = -1;  
    // “  ”  
    private int markedChar = UNMARKED;  
    // “  ”            
    private int readAheadLimit = 0; /* Valid only when markedChar > 0 */ 
 
    // skipLF( skip Line Feed) “       ”    
    private boolean skipLF = false;  
 
    //   “  ” ,   skipLF    
    private boolean markedSkipLF = false;  
 
    //            
    private static int defaultCharBufferSize = 8192;  
    //             
    private static int defaultExpectedLineLength = 80;  
 
    //   “Reader”   BufferedReader  ,sz BufferedReader        
    public BufferedReader(Reader in, int sz) {
       
        super(in);  
        if (sz <= 0)  
            throw new IllegalArgumentException("Buffer size <= 0");  
        this.in = in;  
        cb = new char[sz];  
        nextChar = nChars = 0;  
    }  
 
    //   “Reader”   BufferedReader  ,   BufferedReader      8k  
    public BufferedReader(Reader in) {
       
        this(in, defaultCharBufferSize);  
    }  
 
    //   “BufferedReader”       
    private void ensureOpen() throws IOException {
       
        if (in == null)  
            throw new IOException("Stream closed");  
    }  
 
    //        。          :  
    // (01)         ,  fill()          。  
    // (02)         ,    ,  fill()          。  
    private void fill() throws IOException {
       
        // dst  “cb          ”。  
        int dst;  
        if (markedChar <= UNMARKED) {
       
            //        ,  dst=0。  
            dst = 0;  
        } else {
       
            // delta  “       ”,   “           ”  “     ”   ;  
            int delta = 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 。  
                    char ncb[] = new char[readAheadLimit];  
                    System.arraycopy(cb, markedChar, ncb, 0, delta);  
                    cb = ncb;  
                    markedChar = 0;  
                    dst = delta;  
                }  
                //   nextChar nChars  
                nextChar = nChars = delta;  
            }  
        }  
 
        int n;  
        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       
    public int read() throws IOException {
       
        synchronized (lock) {
       
            ensureOpen();  
            for (;;) {
       
                //  “           ”,  
                //     fill()         
                if (nextChar >= nChars) {
       
                    fill();  
                    if (nextChar >= nChars)  
                        return -1;  
                }  
                //   “     ”,  
                //                  。  
                if (skipLF) {
       
                    skipLF = false;  
                    if (cb[nextChar] == '
'
) { nextChar++; continue; } } // return cb[nextChar++]; } } } // cbuf 。off cbuf ,len private int read1(char[] cbuf, int off, int len) throws IOException { // “ ”, 。 if (nextChar >= nChars) { if (len >= cb.length && markedChar <= UNMARKED && !skipLF) { return in.read(cbuf, off, len); } fill(); } // , ; 。 if (nextChar >= nChars) return -1; // “ ”, if (skipLF) { skipLF = false; if (cb[nextChar] == '
'
) { nextChar++; if (nextChar >= nChars) fill(); if (nextChar >= nChars) return -1; } } // int n = Math.min(len, nChars - nextChar); System.arraycopy(cb, nextChar, cbuf, off, n); nextChar += n; return n; } // read1() , “ ” “ ” public int read(char cbuf[], int off, int len) throws IOException { synchronized (lock) { ensureOpen(); if ((off < 0) || (off > cbuf.length) || (len < 0) || ((off + len) > cbuf.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int n = read1(cbuf, off, len); if (n <= 0) return n; while ((n < len) && in.ready()) { int n1 = read1(cbuf, off + n, len - n); if (n1 <= 0) break; n += n1; } return n; } } // 。ignoreLF “ ” String readLine(boolean ignoreLF) throws IOException { StringBuffer s = null; int startChar; synchronized (lock) { ensureOpen(); boolean omitLF = ignoreLF || skipLF; bufferLoop: for (;;) { if (nextChar >= nChars) fill(); if (nextChar >= nChars) { /* EOF */ if (s != null && s.length() > 0) return s.toString(); else return null; } boolean eol = false; char c = 0; int i; /* Skip a leftover '
', if necessary */
if (omitLF && (cb[nextChar] == '
'
)) nextChar++; skipLF = false; omitLF = false; charLoop: for (i = nextChar; i < nChars; i++) { c = cb[i]; if ((c == '
'
) || (c == '\r')) { eol = true; break charLoop; } } startChar = nextChar; nextChar = i; if (eol) { String str; if (s == null) { str = new String(cb, startChar, i - startChar); } else { s.append(cb, startChar, i - startChar); str = s.toString(); } nextChar++; if (c == '\r') { skipLF = true; } return str; } if (s == null) s = new StringBuffer(defaultExpectedLineLength); s.append(cb, startChar, i - startChar); } } } // 。 public String readLine() throws IOException { return readLine(false); } // n public long skip(long n) throws IOException { if (n < 0L) { throw new IllegalArgumentException("skip value is negative"); } synchronized (lock) { ensureOpen(); long r = n; while (r > 0) { if (nextChar >= nChars) fill(); if (nextChar >= nChars) /* EOF */ break; if (skipLF) { skipLF = false; if (cb[nextChar] == '
'
) { nextChar++; } } long d = nChars - nextChar; if (r <= d) { nextChar += r; r = 0; break; } else { r -= d; nextChar = nChars; } } return n - r; } } // “ ” public boolean ready() throws IOException { synchronized (lock) { ensureOpen(); // true; // , , if (skipLF) { if (nextChar >= nChars && in.ready()) { fill(); } if (nextChar < nChars) { if (cb[nextChar] == '
'
) nextChar++; skipLF = false; } } return (nextChar < nChars) || in.ready(); } } // true。 BufferedReader mark(), reset() public boolean markSupported() { return true; } // BufferedReader 。 readAheadLimit , 。 public void mark(int readAheadLimit) throws IOException { if (readAheadLimit < 0) { throw new IllegalArgumentException("Read-ahead limit < 0"); } synchronized (lock) { ensureOpen(); // readAheadLimit this.readAheadLimit = readAheadLimit; // markedChar = nextChar; // “ ” markedSkipLF = skipLF; } } // BufferedReader , // mark() 。 public void reset() throws IOException { synchronized (lock) { ensureOpen(); if (markedChar < 0) throw new IOException((markedChar == INVALIDATED) ? "Mark invalid" : "Stream not marked"); nextChar = markedChar; skipLF = markedSkipLF; } } public void close() throws IOException { synchronized (lock) { if (in == null) return; in.close(); in = null; cb = null; } } }

설명: BufferReader 의 소스 코드 를 읽 으 려 면 먼저 그의 사상 을 이해 해 야 한다.BufferReader 의 역할 은 다른 Reader 에 버퍼 기능 을 제공 하 는 것 입 니 다.BufferReader 를 만 들 때 구조 함 수 를 통 해 Reader 를 매개 변수 로 지정 합 니 다.BufferReader 는 버퍼 에 일부분 을 읽 을 때마다 이 Reader 의 데 이 터 를 분할 읽 습 니 다.버퍼 의 이 부분 데 이 터 를 조작 한 후 Reader 에서 다음 부분의 데 이 터 를 읽 습 니 다.왜 버퍼 링 이 필요 하지?원인 은 간단 하고 효율 적 인 문제!버퍼 의 데 이 터 는 실제 메모리 에 저장 되 어 있 으 며, 원본 데 이 터 는 하 드 디스크 나 NandFlash 에 저 장 될 수 있 습 니 다.메모리 에서 데 이 터 를 읽 는 속도 가 하 드 디스크 에서 데 이 터 를 읽 는 속도 보다 최소 10 배 이상 빠르다 는 것 을 잘 알 고 있다.그럼 왜 모든 데 이 터 를 한꺼번에 버퍼 에 읽 지 않 았 습 니까?첫째, 모든 데 이 터 를 읽 는 데 걸 리 는 시간 이 길 수 있 습 니 다.둘째, 메모리 가격 이 매우 비 싸 서 용량 이 하드디스크 만큼 크 고 싶 지 않다.
fill () 함수 에 대해 네 가지 상황 으로 나 누 어 설명 합 니 다.
**
상황 1: 버퍼 의 데 이 터 를 읽 었 고 버퍼 가 표시 되 지 않 았 습 니 다.
* * 실행 절 차 는 다음 과 같 습 니 다. (01) 다른 함수 가 fill () 을 호출 하여 버퍼 의 데이터 (02) fill () 실행 코드 if (markedChar < = UNMARKED) {...} 를 업데이트 합 니 다. 분석 하기 편리 하도록 이러한 상황 에서 fill () 이 실행 하 는 작업 은 다음 코드 와 같 습 니 다.
private void fill() throws IOException {
       
    int dst;  
    if (markedChar <= UNMARKED) {
       
        /* No mark */ 
        dst = 0;  
    }   
 
    int n;  
    do {
       
        n = in.read(cb, dst, cb.length - dst);  
    } while (n == 0);  
 
    if (n > 0) {
       
        nChars = dst + n;  
        nextChar = dst;  
    }  
} 

설명: 이러한 상황 이 발생 하 는 상황 은 Reader 에 긴 데이터 가 있 습 니 다. 우 리 는 그 중에서 일부 데 이 터 를 읽 고 버퍼 에서 작업 을 합 니 다.버퍼 에 있 는 데 이 터 를 읽 을 때마다 Buffered Reader 가 표시 되 지 않 습 니 다.그러면 이어서 Reader (BufferReader 가 버퍼 기능 을 제공 하 는 Reader) 에서 다음 부분의 데 이 터 를 버퍼 로 읽 습 니 다.이 가운데 버퍼 에 있 는 데 이 터 를 다 읽 었 는 지 여 부 를 판단 하 는 것 은 'nextChar 와 nChars 사이 의 크기 비교' 를 통 해 판단 된다.그 중에서 nChars 는 버퍼 에 있 는 문자 의 총 갯 수 이 고 nextChar 는 버퍼 에 있 는 다음 읽 을 문자 의 위치 입 니 다.버 프 레 드 리더 가 표 시 됐 는 지 판단 하 는 것 은 'markedChar' 를 통 해 판단 된다.이 사상 을 이해 한 후에 우 리 는 이런 상황 에서 fill () 의 코드 를 분석 하면 특히 이해 하기 쉽다.(01) if (markedChar < = UNMARKED) 는 'Buffered Reader 가 표시 되 었 는 지' 를 판단 하 는 역할 을 합 니 다.표 시 될 경우 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, 그리고 "현재 태그 의 길이" 가 "태그 상한 선 (readAhead Limit)" 을 초과 합 니 다.
* * 실행 절 차 는 다음 과 같 습 니 다. (01) 다른 함수 가 fill () 을 호출 하여 버퍼 의 데이터 (02) fill () 실행 코드 if (della > = readAhead Limit) {...} 를 업데이트 합 니 다. 분석 하기 편리 하도록 이러한 상황 에서 fill () 이 실행 하 는 작업 은 다음 코드 와 같 습 니 다.
private void fill() throws IOException {
       
    int dst;  
    if (markedChar > UNMARKED) {
       
        int delta = nextChar - markedChar;  
        if (delta >= readAheadLimit) {
       
            markedChar = INVALIDATED;  
            readAheadLimit = 0;  
            dst = 0;  
        }   
    }  
 
    int n;  
    do {
       
        n = in.read(cb, dst, cb.length - dst);  
    } while (n == 0);  
    if (n > 0) {
       
        nChars = dst + n;  
        nextChar = dst;  
    }  
} 

설명: 이러한 상황 이 발생 하 는 상황 은 Buffered Reader 에 긴 데이터 가 있 습 니 다. 우 리 는 그 중에서 일부 데 이 터 를 읽 고 버퍼 에 가서 작업 을 합 니 다.버퍼 에 있 는 데 이 터 를 읽 은 후에 BufferedReader 에 태그 가 존재 할 때 '현재 태그 의 길이' 는 '태그 상한 선' 보다 큽 니 다.그럼 상황 2.이 때, 우 리 는 '태그' 를 버 리 고 버퍼 를 업데이트 합 니 다.(01) delta = nextChar - markedChar;그 중에서 dela 는 '현재 표 시 된 길이' 입 니 다. '다음 읽 힌 문자 의 위치' 에서 '표 시 된 위치' 의 차 이 를 줄 입 니 다.(02) if (delta >= readAheadLimit);그 중에서 dela > = readAhead Limit 은 '현재 표 시 된 길이' > = '표시 상한 선' 을 의미 합 니 다.왜 상한 선 을 표시 해 야 합 니까? 즉, read Ahead Limit 의 값 은 도대체 어떤 의미 가 있 습 니까?우리 가 위 치 를 표시 한 후에 버퍼 를 업데이트 할 때 표 시 된 위 치 는 저 장 됩 니 다.버퍼 를 계속 업데이트 할 때 표 시 된 위 치 는 계속 확 대 됩 니 다.그리고 메모리 의 용량 은 유효 합 니 다. 우 리 는 길이 의 저장 표 시 를 제한 하지 않 을 수 없습니다.그래서 표지 의 길 이 를 제한 하기 위해 readAhead Limit 가 필요 합 니 다!(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, "현재 표 시 된 길이" 는 "표시 상한 선 (readAhead Limit)" 을 초과 하지 않 았 으 며, "표시 상한 선 (readAhead Limit)" 은 "버퍼 의 길이" 보다 작 습 니 다.
* * 실행 절 차 는 다음 과 같 습 니 다. (01) 다른 함수 호출 fill () 은 버퍼 의 데이터 (02) fill () 실행 코드 if (readAhead Limit < = cb. length) {..} 를 업데이트 합 니 다. 분석 하기 편리 하도록 이러한 상황 에서 fill () 이 실행 하 는 작업 은 다음 코드 와 같 습 니 다.
private void fill() throws IOException {
       
    int dst;  
    if (markedChar > UNMARKED) {
       
        int delta = nextChar - markedChar;  
        if ((delta < readAheadLimit) &&  (readAheadLimit <= cb.length) ) {
       
            System.arraycopy(cb, markedChar, cb, 0, delta);  
            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;  
    }  
} 

설명: 이러한 상황 이 발생 하 는 상황 은 Buffered Reader 에 긴 데이터 가 있 습 니 다. 우 리 는 그 중에서 일부 데 이 터 를 읽 고 버퍼 에 가서 작업 을 합 니 다.버퍼 에 있 는 데 이 터 를 읽 은 후에 BufferedReader 에 태그 가 존재 할 때 '현재 태그 의 길이' 는 '태그 상한 선' 보다 작고 '태그 상한 선' 은 '버퍼 길이' 보다 작 습 니 다.그럼 상황 3.이때 저 희 는 '표 시 된 위치' (즉, 표 시 된 위치 에서 시 작 된 데 이 터 를 유지 하 는 것) 를 보류 하고 버퍼 를 업데이트 합 니 다 (추 가 된 데 이 터 를 보 존 된 데이터 에 추가 합 니 다).
**
상황 4: 버퍼 의 데 이 터 를 읽 었 습 니 다. 버퍼 의 표시 위치 > 0, "현재 표 시 된 길이" 는 "표시 상한 선 (readAhead Limit)" 을 초과 하지 않 았 고 "표시 상한 선 (readAhead Limit)" 은 "버퍼 의 길이" 보다 큽 니 다.
* * 실행 절 차 는 다음 과 같 습 니 다. (01) 다른 함수 가 fill () 을 호출 하여 버퍼 의 데이터 (02) fill () 실행 코드 else {char ncb [] = new char [readAhead Limit] 를 업데이트 합 니 다...} 분석 하기 편리 하도록 이러한 상황 에서 fill () 이 실행 하 는 작업 은 다음 코드 와 같 습 니 다.
private void fill() throws IOException {
       
    int dst;  
    if (markedChar > UNMARKED) {
       
        int delta = nextChar - markedChar;  
        if ((delta < readAheadLimit) &&  (readAheadLimit > cb.length) ) {
       
            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;  
    }  
} 

설명: 이러한 상황 이 발생 하 는 상황 은 Buffered Reader 에 긴 데이터 가 있 습 니 다. 우 리 는 그 중에서 일부 데 이 터 를 읽 고 버퍼 에 가서 작업 을 합 니 다.버퍼 에 있 는 데 이 터 를 읽 은 후에 BufferedReader 에 태그 가 존재 할 때 '현재 태그 의 길이' 는 '태그 상한 선' 보다 작고 '태그 상한 선' 은 '버퍼 길이' 보다 큽 니 다.그럼 상황이 때 우 리 는 버퍼 의 크기 를 업데이트 한 다음 에 '표 시 된 위치' (즉, 표 시 된 위치 에서 시 작 된 데 이 터 를 유지 하 는 것) 를 유지 하고 버퍼 데 이 터 를 업데이트 해 야 한다.
**
예제 코드
**
  BufferedReader API     ,      (BufferedReaderTest.java)import java.io.BufferedReader;  
import java.io.ByteArrayInputStream;  
import java.io.File;  
import java.io.InputStream;  
import java.io.FileReader;  
import java.io.IOException;  
import java.io.FileNotFoundException;  
import java.lang.SecurityException;  
 
/**  
 * BufferedReader       
 *  
 * @author skywang  
 */ 
public class BufferedReaderTest {
       
 
    private static final int LEN = 5;  
 
    public static void main(String[] args) {
       
        testBufferedReader() ;  
    }  
 
    /**  
     * BufferedReader API      
     */ 
    private static void testBufferedReader() {
       
 
        //   BufferedReader   ,   ArrayLetters    
        try {
       
            File file = new File("bufferedreader.txt");  
            BufferedReader in =  
                  new BufferedReader(  
                      new FileReader(file));  
 
            //        5   。“abcde”  
            for (int i=0; i<LEN; i++) {
       
                //            ,          
                if (in.ready()) {
       
                    //   “         ”  
                    int tmp = in.read();  
                    System.out.printf("%d : %c
"
, i, tmp); } } // “ ” , if (!in.markSupported()) { System.out.println("make not supported!"); return ; } // “ ”, 6 --“f” // 1024 marklimit in.mark(1024); // 22 。 in.skip(22); // 5 char[] buf = new char[LEN]; in.read(buf, 0, LEN); System.out.printf("buf=%s
"
, String.valueOf(buf)); // System.out.printf("readLine=%s
"
, in.readLine()); // “ ” mark() , “f” 。 in.reset(); // “ ” 5 buf 。 “fghij” in.read(buf, 0, LEN); System.out.printf("buf=%s
"
, String.valueOf(buf)); in.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }

프로그램 에서 읽 은 bufferedreader. txt 의 내용 은 다음 과 같 습 니 다.
abcdefghijklmnopqrstuvwxyz  
0123456789 
ABCDEFGHIJKLMNOPQRSTUVWXYZ 

실행 결과:
0 : a
1 : b
2 : c
3 : d
4 : e
buf=01234
readLine=56789
buf=fghij

좋은 웹페이지 즐겨찾기