天天看点

Java IO流之CharArrayWriter和CharArrayReader分析

简介

CharArrayWriter和CharArrayReader分别是字符数组输出流和字符数组输入流,与ByteInputStream和ByteOutputStream字节数组流相比,前者是缓冲区里面存储的是字符,而字节数组里面存储是字节.

  1. CharArrayWriter---字符数组输出流里面有一字符数组(缓冲区),写入字符数组流的数据会存储在此字符数组中,如果字符数据超过了缓冲区的大小,就会扩容.写入流里面的数据可以通过调用方法toString()或者toCharArray()方法查看.
  2. CharArrayReader---字符数组输入流里面有一字符数组(缓冲区),通过有参构造方法将字符数据写入缓冲区,通过调用方法可以读取到缓冲区指定位置的数据.

CharArrayWriter介绍

1.构造方法

public CharArrayWriter() {}
public CharArrayWriter(int initialSize) {}           
  • 无参构造,创建缓冲区为默认的32个字符
  • 有参构造,创建指定大小initialSize的缓冲区

2.内部变量

protected char buf[];
protected int count;
           
  • buf---字符数组输出流中缓冲区,为一字符数组.
  • 缓冲区中有效字符的大小.

3.内部方法

public void write(int c) {}
public void write(char c[], int off, int len) {}
public void write(String str, int off, int len) {}
public void writeTo(Writer out) throws IOException {}
public CharArrayWriter append(CharSequence csq) {}
public CharArrayWriter append(CharSequence csq, int start, int end) {}
public CharArrayWriter append(char c) {}
public void reset() {}
public char toCharArray()[] {}
public int size() {
public String toString() {}           
  • write(int c)---将字符c写到字符数组输出流中.
  • write(char c[] ,int off,int len)---将字符数组c中off位置开始,len个字节写到字符数组输出流中.
  • write(String str,int off,int len)---将字符串中,off位置开始,len个字符写到字符数组输出流中.
  • writeTo(Writer out)---将缓冲区中的数据写到另外一个字符输出流中.
  • append(CharSequence csq)---将字符序列csq写到字符数组输出流中.并返回CharArrayWriter对象
  • append(CharSequence csq,int start ,int end)---将字符序列csq中start到end之间的字符写到数组数组输出流中.并返回CharArrayWriter对象
  • append(char c)---将字符c添加到缓冲区中,并返回CharArrayWriter对象.
  • reset()---清空缓冲区.
  • toCharArray()[]---将缓冲区中数据复制字符数组中,并返回.
  • size()---返回缓冲区的大小.
  • toString()---将缓冲区里面的数据转换成字符串返回.

CharArrayReader介绍

1.构造方法

public CharArrayReader(char buf[]) {}
public CharArrayReader(char buf[], int offset, int length) {}
           
  • 创建指定缓冲区的字符数组输入流.
  • 创建指定缓冲区的字符数组输入流.(会将流中位置和标记的位置都置为offset索引处).

2.内部变量

protected char buf[];
protected int pos;
protected int markedPos = 0;
protected int count;           
  • buf---字符数组输入流中缓冲区位字符数组
  • pos---流中当前位置.
  • markpos---标记位置,调用mark()方法会将当前位置保存到markPos位置,调用reset()方法会将当前重置到markpos位置.
  • count---缓冲区结尾的索引值.

3.内部方法

public int read(){}
public int read(char b[], int off, int len){}
public long skip(long n){}
public boolean ready(){}
public boolean markSupported(){}
public void mark(int readAheadLimit) {}
public void reset(){}
public void close()           
  • read()---读取一个字符.
  • read(char b[],int off,int len)---将流中最多读取len个字节到字节数组b中.位置从off开始.
  • skip(long n)---缓冲区当前位置跳过n个字节,实际跳过的字节小于n.
  • ready()---是否能读取下一个字节.
  • markSupported()---是否支持标记.
  • mark(int readAheadLimit)---标记缓冲区当前位置.保存当前位置索引.
  • reset()---将流中当前位置重置到最后一次标记的位置.
  • close()---关闭流,释放相关资源.

案例

public class CharArrayDemo {
  public static void main(String[] args) throws IOException {
    char[] letters = new char[] {'b','c','d','e','f','h','i','j'};
    testCharArrayWriter(letters);
    System.out.println("-----------------------------");
    testCharArrayReader(letters);
  }

  //测试testCharArrayWriter
  private static void testCharArrayWriter(char[] letters) throws IOException {
    CharArrayWriter caw = new CharArrayWriter();
    caw.write('a');
    caw.write(letters,0,4);
    caw.write("world", 1, 3);
    caw.append('0').append("123").append("678910", 0, 2);
    String result = caw.toString();
    System.out.println("缓冲区数据转化成字符串---"+result);
    char[] charArray = caw.toCharArray();
    System.out.println("缓冲区数据---"+new String(charArray).toString());
    
    CharArrayWriter anotherWriter = new CharArrayWriter();
    caw.writeTo(anotherWriter);
    System.out.println("字符数组anotherWriter的数据"+anotherWriter.toString());
  }
  
  //测试testCharArrayReader
  private static void testCharArrayReader(char[] letters) throws IOException {
    CharArrayReader car = new CharArrayReader(letters);
    //下一个字节是否可读
    if(car.ready()) {
      System.out.println("单个字节---"+(char)car.read());
    }
    char[] letterChar = new char[10];
    car.read(letterChar, 0, 3);
    System.out.println("字符数组letterChar--"+String.valueOf(letterChar));
   //mark(0),0不表示实际意义,mark的是当前位置
    if(car.markSupported()) {
      car.mark(0);
    }
    //跳过三个字节
    car.skip(3);
    System.out.println("此时位置----"+(char)car.read());
    car.reset();
    System.out.println("此时位置----"+(char)car.read());
  }
}           

运行结果:

缓冲区数据转化成字符串---abcdeorl012367

缓冲区数据---abcdeorl012367

字符数组anotherWriter的数据abcdeorl012367

-----------------------------

单个字节---b

字符数组letterChar--cde

源码分析

1.CharArrayWriter源码分析

public class CharArrayWriter extends Writer {
    
    //CharArrayWriter里面缓冲区是一个字符数组
    protected char buf[];

    //缓冲区里面有效字符数
    protected int count;

    //创建一个CharArrayWriter对象,默认缓冲区大小为32
    public CharArrayWriter() {
        this(32);
    }

    //创建一个指定大小initialSize的缓冲区的CharArrayWriter对象.
    public CharArrayWriter(int initialSize) {
        if (initialSize < 0) {
            throw new IllegalArgumentException("Negative initial size: "
                                               + initialSize);
        }
        buf = new char[initialSize];
    }

    //将一个字符写到CharArrayWriter的缓冲区中
    public void write(int c) {
        synchronized (lock) {
            int newcount = count + 1;
            if (newcount > buf.length) {
                buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
            }
            buf[count] = (char)c;
            count = newcount;
        }
    }

    //将字符数组c中off位置开始,len个字节写到CharArrayWriter的缓冲区中.
    public void write(char c[], int off, int len) {
        if ((off < 0) || (off > c.length) || (len < 0) ||
            ((off + len) > c.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }
        synchronized (lock) {
            int newcount = count + len;
            if (newcount > buf.length) {
                buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
            }
            System.arraycopy(c, off, buf, count, len);
            count = newcount;
        }
    }

    //将字符串str里面里面off位置开始len个字符写到缓冲区中
    public void write(String str, int off, int len) {
        synchronized (lock) {
            int newcount = count + len;
            if (newcount > buf.length) {
                buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
            }
            str.getChars(off, off + len, buf, count);
            count = newcount;
        }
    }

    //将缓冲区的数据写到另外一个字符输出流中
    public void writeTo(Writer out) throws IOException {
        synchronized (lock) {
            out.write(buf, 0, count);
        }
    }

    //将字符序列csq数据添加CharArrayWriter里面,返回CharArrayWriter对象
    public CharArrayWriter append(CharSequence csq) {
        String s = (csq == null ? "null" : csq.toString());
        write(s, 0, s.length());
        return this;
    }

    //将字符序列csq中start位置开始到end之间的数据添加到CharArrayWriter的缓冲区里面.返回CharArrayWriter对象
    public CharArrayWriter append(CharSequence csq, int start, int end) {
        String s = (csq == null ? "null" : csq).subSequence(start, end).toString();
        write(s, 0, s.length());
        return this;
    }

    //添加一个字符添加到缓冲区里面,并返回此字符数组输出流对象.
    public CharArrayWriter append(char c) {
        write(c);
        return this;
    }

    //清空缓冲区的数据
    public void reset() {
        count = 0;
    }

    //将缓冲区的数据复制到字符数组,返回字符数组.
    public char toCharArray()[] {
        synchronized (lock) {
            return Arrays.copyOf(buf, count);
        }
    }

    //返回缓冲区有效字符的大小
    public int size() {
        return count;
    }

    //将缓冲区数据转换成字符串
    public String toString() {
        synchronized (lock) {
            return new String(buf, 0, count);
        }
    }
 
    //刷新缓冲区
    public void flush() { }

    //关闭流
    public void close() { }

}           

2.CharArrayReader源码分析

public class CharArrayReader extends Reader {

    //字符数组输入流的缓冲区buf
    protected char buf[];

    //缓冲区中当前的位置
    protected int pos;

    //缓冲区标记的位置
    protected int markedPos = 0;

    //缓冲区结尾的索引值
    protected int count;

    //创建指定缓冲数组的字符输入流
    public CharArrayReader(char buf[]) {
        this.buf = buf;
        this.pos = 0;
        this.count = buf.length;
    }

    //创建一个指定缓冲数组的字符输入流.
    public CharArrayReader(char buf[], int offset, int length) {
        if ((offset < 0) || (offset > buf.length) || (length < 0) ||
            ((offset + length) < 0)) {
            throw new IllegalArgumentException();
        }
        this.buf = buf;
        this.pos = offset;
        this.count = Math.min(offset + length, buf.length);
        this.markedPos = offset;
    }

    /** Checks to make sure that the stream has not been closed */
    //确保流还没有被关闭
    private void ensureOpen() throws IOException {
        if (buf == null)
            throw new IOException("Stream closed");
    }

    //从流中读取一个字符
    public int read() throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (pos >= count)
                return -1;
            else
                return buf[pos++];
        }
    }

    //将缓冲区中的数据读取到字符数组b的off位置开始,长度为len个字符.
    public int read(char b[], int off, int len) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if ((off < 0) || (off > b.length) || (len < 0) ||
                ((off + len) > b.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return 0;
            }

            if (pos >= count) {
                return -1;
            }
            //剩余可读取的字符数avail,实际可能只能读取到avail个
            int avail = count - pos;
            if (len > avail) {
                len = avail;
            }
            if (len <= 0) {
                return 0;
            }
            //将缓冲区字符数组buf中len字节复制到字符数组b中
            System.arraycopy(buf, pos, b, off, len);
            pos += len;
            return len;
        }
    }

    //跳过n个字节.实际跳过的字节要看剩余可读取的字节数.
    //n为负数情况下,返回0
    public long skip(long n) throws IOException {
        synchronized (lock) {
            ensureOpen();
            //实际剩余要读取的字节数
            long avail = count - pos;
            if (n > avail) {
                n = avail;
            }
            if (n < 0) {
                return 0;
            }
            pos += n;
            return n;
        }
    }

    //是否能读取下一个字节
    public boolean ready() throws IOException {
        synchronized (lock) {
            ensureOpen();
            return (count - pos) > 0;
        }
    }

    //是否支持标记
    public boolean markSupported() {
        return true;
    }

    //标记当前位置,保存当前位置的索引
    public void mark(int readAheadLimit) throws IOException {
        synchronized (lock) {
            ensureOpen();
            markedPos = pos;
        }
    }

    //将当前位置重置到最后一次标记位置
    public void reset() throws IOException {
        synchronized (lock) {
            ensureOpen();
            pos = markedPos;
        }
    }

    //关闭流,释放相关资源
    public void close() {
        buf = null;
    }
}           

总结

字符数组输入流和输出流,不是从文件读取和写入到文件的流,字符数组输出流中存在缓冲区,通过调用write()或者append()方法向缓冲区中写入字符数据,当数据超过缓冲区时,会扩容。而字符数组输入流,通过有参构造方法传入字符数组创建缓冲区,调用read(),mark()等方法可以返回缓冲区指定位置索引的数据。

继续阅读