繁体   English   中英

什么是 CharsetDecoder.decode(ByteBuffer, CharBuffer, endOfInput)

[英]What is CharsetDecoder.decode(ByteBuffer, CharBuffer, endOfInput)

CharsetDecoder类有问题。

第一个代码示例(有效):

    final CharsetDecoder dec = Charset.forName("UTF-8").newDecoder();
    final ByteBuffer b = ByteBuffer.allocate(3);
    final byte[] tab = new byte[]{(byte)-30, (byte)-126, (byte)-84}; //char €
    for (int i=0; i<tab.length; i++){
        b.put(tab, i, 1);
    }
    try {
        b.flip();
        System.out.println("a" + dec.decode(b).toString() + "a");
    } catch (CharacterCodingException e1) {
        e1.printStackTrace();
    }

结果是a€a

但是当我执行这段代码时:

    final CharsetDecoder dec = Charset.forName("UTF-8").newDecoder();
    final CharBuffer chars = CharBuffer.allocate(3);
    final byte[] tab = new byte[]{(byte)-30, (byte)-126, (byte)-84}; //char €
    for (int i=0; i<tab.length; i++){
        ByteBuffer buffer = ByteBuffer.wrap(tab, i, 1);
        dec.decode(buffer, chars, i == 2);
    }
    dec.flush(chars);
    System.out.println("a" + chars.toString() + "a");

结果是a

为什么结果不一样?

如何使用CharsetDecoder类的方法decode(ByteBuffer, CharBuffer, endOfInput)来检索结果a€a

- 编辑 -

所以使用 Jesper 的代码我就这样做了。 它并不完美,但可以使用step = 1、2 和 3

final CharsetDecoder dec = Charset.forName("UTF-8").newDecoder();
    final CharBuffer chars = CharBuffer.allocate(6);
    final byte[] tab = new byte[]{(byte)97, (byte)-30, (byte)-126, (byte)-84, (byte)97, (byte)97}; //char €

    final ByteBuffer buffer = ByteBuffer.allocate(10);

    final int step = 3;
    for (int i = 0; i < tab.length; i++) {
        // Add the next byte to the buffer
        buffer.put(tab, i, step);
        i+=step-1;

        // Remember the current position
        final int pos = buffer.position();
        int l=chars.position();

        // Try to decode
        buffer.flip();
        final CoderResult result = dec.decode(buffer, chars, i >= tab.length -1);
        System.out.println(result);

        if (result.isUnderflow() && chars.position() == l) {
            // Underflow, prepare the buffer for more writing
            buffer.position(pos);
        }else{
            if (buffer.position() == buffer.limit()){
                //ByteBuffer decoded
                buffer.clear();
                buffer.position(0);
            }else{
                //a part of ByteBuffer is decoded. We keep only bytes which are not decoded
                final byte[] b = buffer.array();
                final int f = buffer.position();
                final int g = buffer.limit() - buffer.position();
                buffer.clear();
                buffer.position(0);
                buffer.put(b, f, g);
            }
        }
        buffer.limit(buffer.capacity());
    }

    dec.flush(chars);
    chars.flip();

    System.out.println(chars.toString());

方法decode(ByteBuffer, CharBuffer, boolean)返回一个结果,但您忽略了该结果。 如果在第二个代码片段中打印结果:

for (int i = 0; i < tab.length; i++) {
    ByteBuffer buffer = ByteBuffer.wrap(tab, i, 1);
    System.out.println(dec.decode(buffer, chars, i == 2));
}

你会看到这个输出:

UNDERFLOW
MALFORMED[1]
MALFORMED[1]
a   a

如果您在字符中间开始解码,显然它无法正常工作。 解码器期望它读取的第一件事是有效 UTF-8 序列的开始。

编辑- 当解码器报告UNDERFLOW ,它希望您将更多数据添加到输入缓冲区,然后再次尝试调用decode() ,但您必须重新向它提供您所使用的 UTF-8 序列开头的数据试图解码。 您不能在 UTF-8 序列的中间继续。

这是一个有效的版本,在循环的每次迭代中从tab添加一个字节:

final CharsetDecoder dec = Charset.forName("UTF-8").newDecoder();
final CharBuffer chars = CharBuffer.allocate(3);
final byte[] tab = new byte[]{(byte) -30, (byte) -126, (byte) -84}; //char €

final ByteBuffer buffer = ByteBuffer.allocate(10);

for (int i = 0; i < tab.length; i++) {
    // Add the next byte to the buffer
    buffer.put(tab[i]);

    // Remember the current position
    final int pos = buffer.position();

    // Try to decode
    buffer.flip();
    final CoderResult result = dec.decode(buffer, chars, i == 2);
    System.out.println(result);

    if (result.isUnderflow()) {
        // Underflow, prepare the buffer for more writing
        buffer.limit(buffer.capacity());
        buffer.position(pos);
    }
}

dec.flush(chars);
chars.flip();

System.out.println("a" + chars.toString() + "a");

解码器不会在内部缓存来自部分字符的数据,但这并不意味着您必须做复杂的事情才能弄清楚哪些数据要重新馈送到解码器。 您为它提供了一种清晰的方式来表示它实际消耗的数据,即输入 ByteBuffer 及其位置。 在第二个示例中,通过每次给它一个新的 ByteBuffer,OP 未能将解码器传回它报告的尚未消耗的内容。

使用 NIO 缓冲区的标准模式是输入、翻转、输出、紧凑、循环。 缺少优化(这可能为时过早),没有理由自己重新实现紧凑。 您可能只是弄错了,就像@Jesper 和@lecogiteur 所做的那样(如果出现了多个字符)。 您不应该重置到解码调用之前的位置。

第二个例子应该是这样的:

    final CharsetDecoder dec = Charset.forName("UTF-8").newDecoder();
    final CharBuffer chars = CharBuffer.allocate(3);
    final byte[] tab = new byte[]{(byte)-30, (byte)-126, (byte)-84}; //char €
    final ByteBuffer buffer = ByteBuffer.wrap(new byte[3]);

    for (int i=0; i<tab.length; i++){
        b.put(tab, i, 1);  // In actual usage some type of IO read/transfer would occur here
        b.flip();
        dec.decode(buffer, chars, i == 2);
        b.compact();
    }
    dec.flush(chars);
    System.out.println("a" + chars.toString() + "a");

注意:以上不检查返回值以检测格式错误的输入或其他错误处理,以便在任意输入/IO 条件下安全运行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM