为什么Java的CharsetEncoder定义.onMalformedInput()/ CharsetDecoder定义.onUnmappableCharacter()?

时间:2014-04-05 20:53:51

标签: java character-encoding

CharsetDecoder基本上有助于将bytes序列解码为char s序列(请参阅Charset#newDecoder())。另一方面,CharsetEncoder(参见Charset#newEncoder())则相反:采用char s的序列,并将它们编码为byte s的序列。

CharsetDecoder定义.onMalformedInput()并且看似合乎逻辑(某些字节序列可能无法转换为有效的char序列);但为什么.onUnmappableCharacter()因为它的输入是一个字节序列?

同样,CharsetEncoder定义.onUnmappableCharacter(),这里也是逻辑的(例如,如果您的字符集是ASCII,则不能编码ö);但为什么它也定义.onMalformedInput(),因为它的输入是一个字符序列?

更难以理解的是,您无法从解码器获取编码器,反之亦然,这两个类似乎没有一个共同的祖先......


编辑1

确实可以在.onMalformedInput()上触发CharsetEncoder。你"只是"必须提供非法的charchar序列。下面的程序依赖于以下事实:在UTF-16中,高代理人必须遵循低代理人;在这里,使用两个高代理构建一个双元素字符数组,并尝试对其进行编码。 注意如何从这种格式错误的char序列中创建String根本不会引发任何异常

代码:

public static void main(final String... args)
    throws CharacterCodingException
{
    boolean found = false;
    char c = '.';

    for (int i = 0; i < 65536; i++) {
        if (Character.isHighSurrogate((char) i)) {
            c = (char) i;
            found = true;
            break;
        }
    }
    if (!found)
        throw new IllegalStateException();

    System.out.println("found: " + Integer.toHexString(c));
    final char[] foo = { c, c };

    new String(foo); // <-- DOES NOT THROW AN EXCEPTION!!!

    final CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder()
        .onMalformedInput(CodingErrorAction.REPORT);

    encoder.encode(CharBuffer.wrap(foo));
}

输出:

found: d800
Exception in thread "main" java.nio.charset.MalformedInputException: Input length = 1
    at java.nio.charset.CoderResult.throwException(CoderResult.java:277)
    at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:798)
    at com.github.fge.largetext.LargeText.main(LargeText.java:166)

编辑2 但现在,反过来怎么样?来自@ Kairos的答案,引用联机帮助页:

  

UnmappableCharacterException - 如果从输入缓冲区的当前位置开始的字节序列无法映射到等效字符序列,并且当前的不可映射字符操作是CodingErrorAction.REPORT

现在,什么是&#34;无法映射到等效的字符序列&#34;?

我在this project中使用CharsetDecoder s玩了很多,但还没有产生这样的错误。我知道如何重现一个错误,例如,你只有两个字节的三字节UTF-8序列,但这会触发MalformedInputException。在这种情况下,您所要做的就是从ByteBuffer的最后一个已知位置重新开始解码。

触发UnmappableCharacterException基本上意味着字符编码本身将允许生成非法char;或非法的Unicode代码点。

这有可能吗?

1 个答案:

答案 0 :(得分:4)

根据CharsetEncoder.encode()的文档,它声明它会抛出MalformedInputException

  

如果字符序列从输入缓冲区的当前开始   position不是合法的16位Unicode序列和当前的畸形输入   action是CodingErrorAction.REPORT

因此,您可以选择使用CodingErrorAction提供onMalformedInput,以便在遇到其中一个非法的16位Unicode序列时,将执行提供的操作。

同样适用于CharsetDecoder.decode()

  

UnmappableCharacterException - 如果字节序列从   输入缓冲区的当前位置不能映射到等效项   字符序列和当前的不可映射字符操作是   CodingErrorAction.REPORT