【源码学习】Java字符的编码和解码

java中与编码解码相关的类都在包java.nio.charset中,主要有Charset、CharsetDecoder、CharsetEncoder、CoderResult这几个类

  • Charset:字符集
  • CoderResult:状态集
  • CharsetDecoder:编码器
  • CharsetEncoder:解码器

一、可通过Charset获得编码器(encoder)或解码器(decoder)

```java
CharsetEncoder encoder = charset.newEncoder();
CharsetDecoder decoder = charset.newDecoder();
```

二、CoderResult有以下几种状态:

- ByteBuffer 解码完成
    - private static final int CR_UNDERFLOW  = 0;
- CharBuffer 空间不足,解码完成
    - private static final int CR_OVERFLOW   = 1;
    -  private static final int CR_ERROR_MIN  = 2;
- ByteBuffer 解码异常
    -  private static final int CR_MALFORMED  = 2;
- ByteBuffer 超出了 Unicode 定义的范围,eg: 0xD800-0xDFFF 或 >0x10FFFF在编写的时候已经考虑了编解码的问题
    - private static final int CR_UNMAPPABLE = 3;

其中,解码完成和空间不足属于正常状态,解码异常和不能映射到 Unicode 上是异常状态。

对应的CoderResult的状态函数如下所示:

```java
public boolean isUnderflow() { return this.type == 0; }
public boolean isOverflow() { return this.type == 1; }
public boolean isError() { return this.type >= 2; }
public boolean isMalformed() { return this.type == 2; }
public boolean isUnmappable() { return this.type == 3; }
```

三、CharsetDecoder

3.1 该方法返回的是解码的状态码

public final CoderResult decode(ByteBuffer in, CharBuffer out, boolean endOfInput)

核心代码decodeLoop从各缓冲区的当前位置开始进行读取和写入。最多将读取 in.remaining() 个字节,最多将写入 out.remaining() 个字符。之后返回状态码

decode在调用完decodeLoop之后,如果输入缓存还有数据且endOfInput为ture,则修改状态码为输入输出错误,由上层进行处理,否则返回状态码

参数多的方法比参数少的方法多了开始的状态检测:

int newState = endOfInput ? ST_END : ST_CODING;
if ((state != ST_RESET) && (state != ST_CODING) && !(endOfInput && (state == ST_END)))
    throwIllegalStateException(state, newState);
state = newState;

decode的核心代码如下所示:

for (;;) {

    CoderResult cr;
    try {
    	// decodeLoop 完成解码
        cr = decodeLoop(in, out);
    } catch (BufferUnderflowException x) {
        throw new CoderMalfunctionError(x);
    } catch (BufferOverflowException x) {
        throw new CoderMalfunctionError(x);
    }

    // 1. CharBuffer 空间不足
    if (cr.isOverflow())
        return cr;

    // 2. 解码完成,如果 endOfInput=false则会先返回,由调用者继续调用该方法解码
    if (cr.isUnderflow()) {
        if (endOfInput && in.hasRemaining()) {
            cr = CoderResult.malformedForLength(in.remaining());
            // Fall through to malformed-input case
        } else {
            return cr;
        }
    }

    // 3. 解码出现异常时的处理,默认为 REPORT,即由上层处理异常
    CodingErrorAction action = null;
    if (cr.isMalformed())
        action = malformedInputAction;
    else if (cr.isUnmappable())
        action = unmappableCharacterAction;
    else
        assert false : cr.toString();

    // 3.1 REPORT 由上层处理异常
    if (action == CodingErrorAction.REPORT)
        return cr;
    // 3.2 REPLACE 追加了 replacement 字符 
    if (action == CodingErrorAction.REPLACE) {
        if (out.remaining() < replacement.length())
            return CoderResult.OVERFLOW;
        out.put(replacement);
    }
    // 3.3 IGNORE 和 REPLACE 都忽略这种异常继续解码
    if ((action == CodingErrorAction.IGNORE)
        || (action == CodingErrorAction.REPLACE)) {
        // Skip erroneous input either way
        in.position(in.position() + cr.length());
        continue;
    }

    assert false;
}

3.2 该方法返回的是解码之后的缓存

public final CharBuffer decode(ByteBuffer in)
该方法循环调用decode(in, out, true),直到解码完成
for (;;) {
    CoderResult cr = in.hasRemaining() ? decode(in, out, true) : CoderResult.UNDERFLOW;

    // 1. 解码完成,调用 flush 并结束
    if (cr.isUnderflow())
        cr = flush(out);
    if (cr.isUnderflow())
        break;

    // 2. CharBuffer 空间不足,扩容,继续解码
    if (cr.isOverflow()) {
        n = 2*n + 1;    // Ensure progress; n might be 0!
        CharBuffer o = CharBuffer.allocate(n);
        out.flip();
        o.put(out);
        out = o;
        continue;
    }

    // 3. 解码异常,直接抛出
    cr.throwException();
}
out.flip();

flush主要做了一个状态转换

if (this.state == 2) {
    CoderResult cr = this.implFlush(out);
    if (cr.isUnderflow()) {
        this.state = 3;
    }
    return cr;
}

缓冲的flip()用于切换读写状态

四、CharsetEncoder

CharsetEncoder的原理和CharsetDecoder很像,就不赘述了

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值