Reader、Writer、BufferedWriter源碼解析

Text streams – Interacts as Unicode characters which are 16 bits

一、Characters in Unicode

Today, many applications use Unicode (UTF-8 or UTF-16) to store text data. It may take one or more bytes to represent a single character in UTF-8. In UTF-16 each character takes 2 bytes to represent. Therefore, when reading text data, a single byte in the data may not correspond to one character in UTF. If you just read one byte at a time of UTF-8 data via an InputStream and try to convert each byte into a char, you may not end up with the text you expected.

To solve this problem we have the Reader class. The Reader class is capable of decoding bytes into characters. You need to tell the Reader what character set to decode. This is done when you instantiate the Reader (actually, when you instantiate one of its subclasses).

writer()或Reader調用的是StreamEncoder

二、Reader

1.方法(4種)

    /*
      Reads a single character.  This method will block until a character is
      available, an I/O error occurs, or the end of the stream is reached.
    
     @return : The character read, as an integer in the range 0 to 65535
                (因爲char是兩個字節,所以是0-2^16)
               , or -1 if the end of the stream has been reached
     */
    public int read() throws IOException {
        char cb[] = new char[1];
        if (read(cb, 0, 1) == -1)
            return -1;
        else
            return cb[0];
    }
   /**
      Reads characters into a portion of an array. 
      @param      cbuf  Destination buffer
      @param      off   Offset at which to start storing characters
      @param      len   Maximum number of characters to read
      @return     The number of characters read,
                  (0-Integer.MAX_VALUE)最大值是int的最大邊界
                  or -1       
     */
public int read(char cbuf[], int off, int len) 

public int read(char cbuf[])

  /*
     * Attempts to read characters into the specified character buffer.
     * The buffer is used as a repository of characters as-is: the only
     * changes made are the results of a put operation. No flipping or
     * rewinding of the buffer is performed.
     
     */
    public int read(java.nio.CharBuffer target) throws IOException {
        int len = target.remaining();
        char[] cbuf = new char[len];
        int n = read(cbuf, 0, len);
        if (n > 0)
            target.put(cbuf, 0, n);
        return n;
    }

2.實例

  InputStream is=new FileInputStream("C:\\Users\\ASUS\\Desktop\\tmp.txt");
      InputStreamReader isr=new InputStreamReader(is);
      int ch=0;
   /*  while((ch=isr.read())!=-1)
        {
            System.out.println((char)ch);
        }
*/
      
      char[] tmp=new char[2];
        while((ch=isr.read(tmp))!=-1)
        {
            System.out.println(new String(tmp));
        }

三、Writer

在這裏插入圖片描述
涉及到的CharBuffer、ByteBuffer操作參考:https://www.iteye.com/blog/zachary-guo-1457542

   FileOutputStream fos=new FileOutputStream("C:\\Users\\ASUS\\Desktop\\tmp.txt");
        Writer isr=new OutputStreamWriter(fos);
        isr.write('D');
        isr.flush();
        isr.close();

1.構造函數

核心:創建一個StreamEncoder對象

public class OutputStreamWriter extends Writer {
    private final StreamEncoder se;
    
//Creates an OutputStreamWriter that uses the default character encoding.
    public OutputStreamWriter(OutputStream out) {
        super(out); //Writer(Object lock)//保證寫時,線程安全。
        try {
        
            se = StreamEncoder.forOutputStreamWriter(out, this, (String)null);
        } catch (UnsupportedEncodingException e) {
            throw new Error(e);
        }
    } 
//也就是OutputStreamWriter的writer()實際調用的是StreamEncoder中的writer()
  public void write(int c) throws IOException {
        se.write(c);
    }
//也就是OutputStreamWriter的flush()實際調用的是StreamEncoder中的flush()
   public void flush() throws IOException {
        se.flush();
    }
  }
//也就是OutputStreamWriter的close()實際調用的是StreamEncoder中的close()
 public void close() throws IOException {
        se.close();
    }
public class StreamEncoder extends Writer {
private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;
    private volatile boolean isOpen;
    private Charset cs;
    private CharsetEncoder encoder;
    private ByteBuffer bb;
    private final OutputStream out;
    private WritableByteChannel ch;
    private boolean haveLeftoverChar;
    private char leftoverChar;
    private CharBuffer lcb;
    
//以OutputStream、OutputStreamWriter、合法charset來創建StreamEncoder對象
  public static StreamEncoder forOutputStreamWriter(OutputStream var0, Object var1, 
  String var2) throws UnsupportedEncodingException {
        String var3 = var2;
        //參數未指定字符集,則採用JVM默認字符集
        if (var2 == null) {
            var3 = Charset.defaultCharset().name();
        }
          
        try {//檢查charset合法性
            if (Charset.isSupported(var3)) {
                return new StreamEncoder(var0, var1, Charset.forName(var3));
            }
        } catch (IllegalCharsetNameException var5) {
        }
        throw new UnsupportedEncodingException(var3);
    }
    
    。。。。最終會調用下面的構造器:  
  //CharsetEncoder 字符編碼方式(字符轉換爲二進制)
  private StreamEncoder(OutputStream var1, Object var2, CharsetEncoder var3) {
        super(var2);//Writer(Object lock)//保證寫時,線程安全。
        this.isOpen = true;
   //比如調用read() 時讀了2個字符,但只能展示1個,所以會剩下一個字符,若下次再讀,直接取即可
        this.haveLeftoverChar = false;
        this.lcb = null;
        this.out = var1;
        this.ch = null;
        this.cs = var3.charset();//指定字符集
        this.encoder = var3;
        if (this.ch == null) {
            this.bb = ByteBuffer.allocate(8192);//創建緩衝區對象,並分配空間
        }
    }

2.write

isr.write(); 完成的工作是將字符轉換成字節,並存儲在StreamEncoder 的private ByteBuffer bb;中。並沒有涉及寫到流操作

public class StreamEncoder extends Writer {
 public void write(int var1) throws IOException {
        char[] var2 = new char[]{(char)var1};
        this.write((char[])var2, 0, 1);
    }

    public void write(char[] var1, int var2, int var3) throws IOException {
        synchronized(this.lock)
        {
            this.ensureOpen();
            if (var2 >= 0 && var2 <= var1.length && var3 >= 0 && var2 + var3 <= var1.length && var2 + var3 >= 0) {
                if (var3 != 0) {
                    this.implWrite(var1, var2, var3);
                }
            } else {
                throw new IndexOutOfBoundsException();
            }
        }
    }
}
//將char[]數組寫入StreamEncoder的ByteBuffer中(也就是完成字符到字節的轉換)
 void implWrite(char[] var1, int var2, int var3) throws IOException {
 //wrap:創建緩衝區對象,但並不分配空間,position=var2,limit=var2+var3
        CharBuffer var4 = CharBuffer.wrap(var1, var2, var3);
        if (this.haveLeftoverChar) {
            this.flushLeftoverChar(var4, false);
        }
        //hasRemaining:position < limit;
        while(var4.hasRemaining()) {
   //核心代碼encode:(CharBuffer in,ByteBuffer out,boolean endOfInput)會將結果寫入out中
   //因爲bb改變,執行完position爲var2+var3,limit不變爲8192
            CoderResult var5 = this.encoder.encode(var4, this.bb, false);
            if (var5.isUnderflow()) {
                assert var4.remaining() <= 1 : var4.remaining();

                if (var4.remaining() == 1) {
                    this.haveLeftoverChar = true;
                    this.leftoverChar = var4.get();
                }
                break;
            }

            if (var5.isOverflow()) {
                assert this.bb.position() > 0;

                this.writeBytes();
            } else {
                var5.throwException();
            }
        }

    }

append()與write區別

/* append內部還是要調用writer 只有細微差別 ①返回值  ②參數類型更廣泛
   CharSequence(已知實現類:CharBuffer、String、StringBuffer、StringBuilder、Segment)
 */
  public Writer append(char c) throws IOException {
        write(c);
        return this;
    }

3.flush()

會將StreamEncoder的private ByteBuffer bb;轉換成byte[],再調用OutputStream的write寫入輸出流中

public class StreamEncoder extends Writer {
 public void flush() throws IOException {
        synchronized(this.lock) {
            this.ensureOpen();
            this.implFlush();
        }
    }
    
void implFlush() throws IOException {
        this.implFlushBuffer();
        if (this.out != null) {
            this.out.flush(); //FileOutputStream依然使用父類的flush,do-nothing
        }

    }

void implFlushBuffer() throws IOException {
        if (this.bb.position() > 0) {
            this.writeBytes();
        }

    }
 private void writeBytes() throws IOException {
 //因爲在2.write的implWrite中將bb 改變position之前的爲此次要寫入流中的數據
 //所以通過flip方法 position=0,limit爲position 也就是隻看有效數據。
        this.bb.flip();
        int var1 = this.bb.limit();
        int var2 = this.bb.position();

        assert var2 <= var1;

        int var3 = var2 <= var1 ? var1 - var2 : 0;
        if (var3 > 0) {
            if (this.ch != null) {
                assert this.ch.write(this.bb) == var3 : var3;
            } else {
    //核心代碼:write(byte b[], int off, int len) 此時調用OutputStream的寫操作
                this.out.write(this.bb.array(), this.bb.arrayOffset() + var2, var3);
            }
        }

        this.bb.clear();
    }

4.close

StreamEncoder.close會將out同時關閉,所以不用再重複寫。

四、BufferedWriter

與BufferedOutputStream類似,提供個字符數組char cb[] ,當字符數到達一定數目後,才調用Writer的write方法。BufferedWriter支持行寫newLine()。

public class BufferedWriter extends Writer {

    private Writer out;
    private char cb[];
    private int nChars, nextChar;
    private String lineSeparator; //系統是\r\n
    private static int defaultCharBufferSize = 8192;
  public void write(int c) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (nextChar >= nChars)//滿,則write
                flushBuffer();
            cb[nextChar++] = (char) c;
        }
    }
    
 void flushBuffer() throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (nextChar == 0)
                return;
           //核心代碼調用writer實現類的write()比如 OutputStream的write()
         out.write(cb, 0, nextChar); //Writer out; 不要和三中的OutputStream out;混淆
         nextChar = 0;
        }
    }
   
      public void newLine() throws IOException {
        write(lineSeparator); 
    }

測試:

 FileOutputStream fos=new FileOutputStream("C:\\Users\\ASUS\\Desktop\\tmp.txt");
      Writer isr=new OutputStreamWriter(fos);
      BufferedWriter bufw=new BufferedWriter(isr,1);
        bufw.write("abcde");
        bufw.newLine();

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章