java io系列18之 CharArrayReader(字符数组输入流)
从本章开始,我们开始对java io中的“字符流”进行学习。首先,要学习的是CharArrayReader。学习时,我们先对CharArrayReader有个大致了解,然后深入了解一下它的源码,最后通过示例来掌握它的用法。
转载请注明出处:http://www.cnblogs.com/skywang12345/p/io_18.html
CharArrayReader 介绍
CharArrayReader 是字符数组输入流。它和ByteArrayInputStream类似,只不过ByteArrayInputStream是字节数组输入流,而CharArray是字符数组输入流。CharArrayReader 是用于读取字符数组,它继承于Reader。操作的数据是以字符为单位!
CharArrayReader 函数列表
CharArrayReader(char[] buf)
CharArrayReader(char[] buf, int offset, int length)
void close()
void mark(int readLimit)
boolean markSupported()
int read()
int read(char[] buffer, int offset, int len)
boolean ready()
void reset()
long skip(long charCount)
Reader和CharArrayReader源码分析
Reader是CharArrayReader的父类,我们先看看Reader的源码,然后再学CharArrayReader的源码。
1. Reader源码分析(基于jdk1.7.40)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 package java.io;
2
3 public abstract class Reader implements Readable, Closeable {
4
5 protected Object lock;
6
7 protected Reader() {
8 this.lock = this;
9 }
10
11 protected Reader(Object lock) {
12 if (lock == null) {
13 throw new NullPointerException();
14 }
15 this.lock = lock;
16 }
17
18 public int read(java.nio.CharBuffer target) throws IOException {
19 int len = target.remaining();
20 char[] cbuf = new char[len];
21 int n = read(cbuf, 0, len);
22 if (n > 0)
23 target.put(cbuf, 0, n);
24 return n;
25 }
26
27 public int read() throws IOException {
28 char cb[] = new char[1];
29 if (read(cb, 0, 1) == -1)
30 return -1;
31 else
32 return cb[0];
33 }
34
35 public int read(char cbuf[]) throws IOException {
36 return read(cbuf, 0, cbuf.length);
37 }
38
39 abstract public int read(char cbuf[], int off, int len) throws IOException;
40
41 private static final int maxSkipBufferSize = 8192;
42
43 private char skipBuffer[] = null;
44
45 public long skip(long n) throws IOException {
46 if (n < 0L)
47 throw new IllegalArgumentException("skip value is negative");
48 int nn = (int) Math.min(n, maxSkipBufferSize);
49 synchronized (lock) {
50 if ((skipBuffer == null) || (skipBuffer.length < nn))
51 skipBuffer = new char[nn];
52 long r = n;
53 while (r > 0) {
54 int nc = read(skipBuffer, 0, (int)Math.min(r, nn));
55 if (nc == -1)
56 break;
57 r -= nc;
58 }
59 return n - r;
60 }
61 }
62
63 public boolean ready() throws IOException {
64 return false;
65 }
66
67 public boolean markSupported() {
68 return false;
69 }
70
71 public void mark(int readAheadLimit) throws IOException {
72 throw new IOException("mark() not supported");
73 }
74
75 public void reset() throws IOException {
76 throw new IOException("reset() not supported");
77 }
78
79 abstract public void close() throws IOException;
80 }
2. CharArrayReader 源码分析(基于jdk1.7.40)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 package java.io;
2
3 public class CharArrayReader extends Reader {
4 // 字符数组缓冲
5 protected char buf[];
6
7 // 下一个被获取的字符的位置
8 protected int pos;
9
10 // 被标记的位置
11 protected int markedPos = 0;
12
13 // 字符缓冲的长度
14 protected int count;
15
16 // 构造函数
17 public CharArrayReader(char buf[]) {
18 this.buf = buf;
19 this.pos = 0;
20 this.count = buf.length;
21 }
22
23 // 构造函数
24 public CharArrayReader(char buf[], int offset, int length) {
25 if ((offset < 0) || (offset > buf.length) || (length < 0) ||
26 ((offset + length) < 0)) {
27 throw new IllegalArgumentException();
28 }
29 this.buf = buf;
30 this.pos = offset;
31 this.count = Math.min(offset + length, buf.length);
32 this.markedPos = offset;
33 }
34
35 // 判断“CharArrayReader是否有效”。
36 // 若字符缓冲为null,则认为其无效。
37 private void ensureOpen() throws IOException {
38 if (buf == null)
39 throw new IOException("Stream closed");
40 }
41
42 // 读取下一个字符。即返回字符缓冲区中下一位置的值。
43 // 注意:读取的是字符!
44 public int read() throws IOException {
45 synchronized (lock) {
46 ensureOpen();
47 if (pos >= count)
48 return -1;
49 else
50 return buf[pos++];
51 }
52 }
53
54 // 读取数据,并保存到字符数组b中从off开始的位置中,len是读取长度。
55 public int read(char b[], int off, int len) throws IOException {
56 synchronized (lock) {
57 ensureOpen();
58 if ((off < 0) || (off > b.length) || (len < 0) ||
59 ((off + len) > b.length) || ((off + len) < 0)) {
60 throw new IndexOutOfBoundsException();
61 } else if (len == 0) {
62 return 0;
63 }
64
65 if (pos >= count) {
66 return -1;
67 }
68 if (pos + len > count) {
69 len = count - pos;
70 }
71 if (len <= 0) {
72 return 0;
73 }
74 System.arraycopy(buf, pos, b, off, len);
75 pos += len;
76 return len;
77 }
78 }
79
80 // 跳过n个字符
81 public long skip(long n) throws IOException {
82 synchronized (lock) {
83 ensureOpen();
84 if (pos + n > count) {
85 n = count - pos;
86 }
87 if (n < 0) {
88 return 0;
89 }
90 pos += n;
91 return n;
92 }
93 }
94
95 // 判断“是否能读取下一个字符”。能的话,返回true。
96 public boolean ready() throws IOException {
97 synchronized (lock) {
98 ensureOpen();
99 return (count - pos) > 0;
100 }
101 }
102
103 public boolean markSupported() {
104 return true;
105 }
106
107 // 保存当前位置。readAheadLimit在此处没有任何实际意义
108 // mark()必须和reset()配合使用才有意义!
109 public void mark(int readAheadLimit) throws IOException {
110 synchronized (lock) {
111 ensureOpen();
112 markedPos = pos;
113 }
114 }
115
116 // 重置“下一个读取位置”为“mark所标记的位置”
117 public void reset() throws IOException {
118 synchronized (lock) {
119 ensureOpen();
120 pos = markedPos;
121 }
122 }
123
124 public void close() {
125 buf = null;
126 }
127 }
说明:
CharArrayReader实际上是通过“字符数组”去保存数据。
(01) 通过 CharArrayReader(char[] buf) 或 CharArrayReader(char[] buf, int offset, int length) ,我们可以根据buf数组来创建CharArrayReader对象。
(02) read()的作用是从CharArrayReader中“读取下一个字符”。
(03) read(char[] buffer, int offset, int len)的作用是从CharArrayReader读取字符数据,并写入到字符数组buffer中。offset是将字符写入到buffer的起始位置,len是写入的字符的长度。
(04) markSupported()是判断CharArrayReader是否支持“标记功能”。它始终返回true。
(05)
mark(int
readlimit)的作用是记录标记位置。记录标记位置之后,某一时刻调用reset()则将“CharArrayReader下一个被读取的位置”重
置到“mark(int
readlimit)所标记的位置”;也就是说,reset()之后再读取CharArrayReader时,是从mark(int
readlimit)所标记的位置开始读取。
示例代码
关于CharArrayReader中API的详细用法,参考示例代码(CharArrayReaderTest.java):
1 /**
2 * CharArrayReader 演示程序
3 *
4 * @author skywang
5 */
6
7 import java.io.CharArrayReader;
8 import java.io.CharArrayWriter;
9 import java.io.IOException;
10
11 public class CharArrayReaderTest {
12
13 private static final int LEN = 5;
14 // 对应英文字母“abcdefghijklmnopqrstuvwxyz”
15 private static final char[] ArrayLetters = new char[] {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
16
17 public static void main(String[] args) {
18 tesCharArrayReader() ;
19 }
20
21 /**
22 * CharArrayReader的API测试函数
23 */
24 private static void tesCharArrayReader() {
25 try {
26 // 创建CharArrayReader字符流,内容是ArrayLetters数组
27 CharArrayReader car = new CharArrayReader(ArrayLetters);
28
29 // 从字符数组流中读取5个字符
30 for (int i=0; i<LEN; i++) {
31 // 若能继续读取下一个字符,则读取下一个字符
32 if (car.ready() == true) {
33 // 读取“字符流的下一个字符”
34 char tmp = (char)car.read();
35 System.out.printf("%d : %c\n", i, tmp);
36 }
37 }
38
39 // 若“该字符流”不支持标记功能,则直接退出
40 if (!car.markSupported()) {
41 System.out.println("make not supported!");
42 return ;
43 }
44
45 // 标记“字符流中下一个被读取的位置”。即--标记“f”,因为因为前面已经读取了5个字符,所以下一个被读取的位置是第6个字符”
46 // (01), CharArrayReader类的mark(0)函数中的“参数0”是没有实际意义的。
47 // (02), mark()与reset()是配套的,reset()会将“字符流中下一个被读取的位置”重置为“mark()中所保存的位置”
48 car.mark(0);
49
50 // 跳过5个字符。跳过5个字符后,字符流中下一个被读取的值应该是“k”。
51 car.skip(5);
52
53 // 从字符流中读取5个数据。即读取“klmno”
54 char[] buf = new char[LEN];
55 car.read(buf, 0, LEN);
56 System.out.printf("buf=%s\n", String.valueOf(buf));
57
58 // 重置“字符流”:即,将“字符流中下一个被读取的位置”重置到“mark()所标记的位置”,即f。
59 car.reset();
60 // 从“重置后的字符流”中读取5个字符到buf中。即读取“fghij”
61 car.read(buf, 0, LEN);
62 System.out.printf("buf=%s\n", String.valueOf(buf));
63 } catch (IOException e) {
64 e.printStackTrace();
65 }
66 }
67 }
运行结果:
0 : a
1 : b
2 : c
3 : d
4 : e
buf=klmno
buf=fghij