javareadline编码_Java字符编码(三)Reader中的编解码Java 字符编码(三)Reader 中的编解码
我们知道 BufferedReader 可以将字节流转化为字符流,那它是如何编解码的呢?
try (BufferedReader reader = new BufferedReader(new FileReader(...));) {
String line;
while ((line = adLine()) != null) {
System.out.println(line);
}
}
⼀、Reader
1.1 Reader
Reader 中有四个重载的 read ⽅法:
/
/ 读到 CharBuffer 中
public int read(java.nio.CharBuffer target) throws IOException {
int len = aining();
char[] cbuf = new char[len];
int n = read(cbuf, 0, len);
if (n > 0)
target.put(cbuf, 0, n);
return n;
}
// 读⼀个字符
public int read() throws IOException {
char cb[] = new char[1];
if (read(cb, 0, 1) == -1)
return -1;
else
return cb[0];
}
// 读多个字符
public int read(char cbuf[]) throws IOException {
return read(cbuf, 0, cbuf.length);
}
// 由⼦类实现
public abstract int read(char cbuf[], int off, int len) throws IOException;
1.2 Reader 类图
BufferedReader -> InputStreamReader -> StreamDecoder -> InputStream。真正处理编解码的是 StreamDecoder 类。⼆、StreamDecoder
2.1 read()⽅法
返回读取的⼀个字符,当读到⽂件末尾时返回 -1。
public int read() throws IOException {
return read0();
}
// read0 每次会读取 2 个字符,但 read0 只会返回⼀个字符
java语言使用的字符码集是
// 因此会将多读的另⼀个字符先存储在 leftoverChar 中
private int read0() throws IOException {
synchronized (lock) {
// 1. 如果上次读的两个字符还剩下的⼀个未返回,直接返回即可
if (haveLeftoverChar) {
haveLeftoverChar = false;
return leftoverChar;
}
/
/ 2. 每次读取两个字符,返回第⼀个字符,另⼀个存储在 leftoverChar 中
char cb[] = new char[2];
int n = read(cb, 0, 2);
switch (n) {
// 2.1 ⽂件流已读完,直接返回
case -1:
return -1;
// 2.2 如果读取到 2 个字符,则返回第⼀个,缓存第⼆个
leftoverChar = cb[1];
haveLeftoverChar = true;
// FALL THROUGH
case 1:
return cb[0];
default:
assert false : n;
return -1;
}
}
}
read0 ⽅法每次读 2 个字符,为什么不是 1 个或者多个?⾸先多个可以使⽤ read(char cbuf[], int offset, int length) ⽅法,其次当实际要读取的字符为 len=1 时会直接调⽤ read0 ⽅法,即回调 read(cb[], 0, 2)。
2.2 read(char cbuf[], int offset, int length)⽅法
该⽅法最多读取 length 个字节放⼊字符数组中,从字符数组的偏移量 offset 开始存储,返回实际读取存储的字节数,当读取到⽂件末尾时,返回 -1。
public int read(char cbuf[], int offset, int length) throws IOException {
int off = offset;
int len = length;
synchronized (lock) {
ensureOpen();
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
}
if (len == 0)
return 0;
int n = 0;
// 1. ⾸先取出 leftoverChar
if (haveLeftoverChar) {
// Copy the leftover char into the buffer
cbuf[off] = leftoverChar;
off++; len--;
haveLeftoverChar = false;
if ((len == 0) || !implReady())
// Return now if this is all we can produce w/o blocking
return n;
}
// 2. 只读取⼀个则直接调⽤ read0 ⽅法,即回调 read(cb[], 0, 2),如果 read0 的 length=1 会循环递归
// 这时 length=2 不会进⼊ if 分⽀,直接调⽤ implRead ⽅法
if (len == 1) {
// Treat single-character array reads just like read()
int c = read0();
if (c == -1)
return (n == 0) ? -1 : n;
cbuf[off] = (char)c;
return n + 1;
}
/
/ 3. implRead 真正⽤于读取字节流到字符流 cbuf 中,返回实际读取的字符数
return n + implRead(cbuf, off, off + len);
}
}
2.3 implRead(cbuf, off, end)
读取字符到数组中,从数组的偏移量 offset 开始存储,最多存储到偏移量 end,返回实际读取存储的字符个数。int implRead(char[] cbuf, int off, int end) throws IOException {
// 1. 每次最少读取 2 个字符
assert (end - off > 1);
//2. 将字符数组包装到缓冲区中,缓冲区修改,字符数组也会被修改
// cb 本质理解为⼀个数组,当前位置为 off,界限为 end-off
CharBuffer cb = CharBuffer.wrap(cbuf, off, end - off);
if (cb.position() != 0)
// Ensure that cb[0] == cbuf[off]
// slice 不会修改 cbuf 字符数组,只修改了 cb 指针位置,即忽略了 cubf[] 中已经有的字符
cb = cb.slice();
// 3. 将 readBytes 读到缓冲区 bb 中的字节解码到 cb 中
boolean eof = false;
for (;;) {
// 3.1 将字节缓冲区 bb 中解码到字符缓冲区 cb 中
CoderResult cr = decoder.decode(bb, cb, eof);
// 3.2 解码成功
if (cr.isUnderflow()) {
/
/ 流中数据读取完毕或 cb 没有空间了就直接返回
if (eof)
break;
if (!cb.hasRemaining())
break;
// 如果流不能读取⽽ cb 已经有部分解码成功就直接返回,否则调⽤ readBytes 等待流的读取
if ((cb.position() > 0) && !inReady())
break; // Block at most once
// 从流中读取数据到 bb 中,如果 n<0 则数据读取完毕,但 bb 中还有数据的话会尽量再进⾏⼀次解码int n = readBytes();
if (n < 0) {
eof = true;
if ((cb.position() == 0) && (!bb.hasRemaining()))
break;
}
continue;
}
// 3.3 cb 中没有空间了,返回由上层扩容处理
if (cr.isOverflow()) {
assert cb.position() > 0;
break;
}
/
/ 3.4 解码异常
cr.throwException();
}
// 4. 清空 decoder 状态
if (eof) {
// ## Need to flush decoder

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。