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

fge*_*fge 6 java character-encoding

A CharsetDecoder基本上有助于将序列解码byteschars 序列(参见参考资料Charset#newDecoder()).另一方面,a CharsetEncoder(参见Charset#newEncoder())反过来:取一系列chars,并将它们编码为bytes 序列.

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

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

这是更有趣的,你不能从解码器获得编码器,反之亦然,这两个类似乎没有共享一个共同的祖先......


编辑1

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

码:

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));
}
Run Code Online (Sandbox Code Playgroud)

输出:

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)
Run Code Online (Sandbox Code Playgroud)

编辑2但现在,反过来怎么样?从@ Kairos的回答中,引用手册页:

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

现在,什么是"无法映射到等效的字符序列"?

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

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

这有可能吗?

Hyp*_*cle 4

根据CharsetEncoder.encode()的文档,它指出它抛出 MalformedInputException

如果从输入缓冲区当前位置开始的字符序列不是合法的 16 位 Unicode 序列,并且当前格式错误的输入操作是 CodingErrorAction.REPORT

因此,您可以选择利用onMalformedInput提供CodingErrorAction,这样,如果您遇到这些非法的 16 位 Unicode 序列之一,将执行所提供的操作。

类似地对于CharsetDecoder.decode()

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