问题 在Java中从Windows 1252转换为UTF8:使用CharsetDecoder / Encoder的空字符


我知道这是一个非常普遍的问题,但我变得疯了。

我用过这段代码:

String ucs2Content = new String(bufferToConvert, inputEncoding);        
        byte[] outputBuf = ucs2Content.getBytes(outputEncoding);        
        return outputBuf;

但我读到最好使用CharsetDecoder和CharsetEncoder(我的内容有一些字符可能在目标编码之外)。我刚刚写了这段代码,但是有一些问题:

// Create the encoder and decoder for Win1252
Charset charsetInput = Charset.forName(inputEncoding);
CharsetDecoder decoder = charsetInput.newDecoder();

Charset charsetOutput = Charset.forName(outputEncoding);
CharsetEncoder encoder = charsetOutput.newEncoder();

// Convert the byte array from starting inputEncoding into UCS2
CharBuffer cbuf = decoder.decode(ByteBuffer.wrap(bufferToConvert));

// Convert the internal UCS2 representation into outputEncoding
ByteBuffer bbuf = encoder.encode(CharBuffer.wrap(cbuf));
return bbuf.array();

确实这个代码 向缓冲区附加一个空字符序列!!!!!

有人能告诉我问题出在哪里?我不熟悉Java中的编码转换。

有没有更好的方法来转换Java中的编码?


2600
2018-05-25 16:11


起源



答案:


你的问题是 ByteBuffer.array() 返回对用作ByteBuffer的后备存储的数组的直接引用,而不是后备数组的有效范围的副本。你必须服从 bbuf.limit() (正如Peter在他的回答中所做的那样)并且只使用索引中的数组内容 0 至 bbuf.limit()-1

后备数组中额外0值的原因是CharsetEncoder创建生成的ByteBuffer的方式存在轻微缺陷。每个CharsetEncoder都有一个“每个字符的平均字节数”,对于UCS2编码器来说,它看起来简单而正确(2个字节/字符)。遵循该固定值,CharsetEncoder最初分配具有“字符串长度*每个字符的平均字节数”字节的ByteBuffer,在这种情况下,例如, 10个字符长的字符串为20个字节。然而,UCS2 CharsetEncoder以BOM(字节顺序标记)开始,它也占用2个字节,因此10个字符中只有9个符合分配的ByteBuffer。 CharsetEncoder检测溢出并分配长度为2 * n + 1的新ByteBuffer(n是ByteBuffer的原始长度),在这种情况下为2 * 20 + 1 = 41字节。由于21个新字节中只有2个需要对剩余字符进行编码,因此您可以使用该数组 bbuf.array() 将具有41个字节的长度,但是 bbuf.limit() 将指示实际仅使用前22个条目。


7
2018-05-26 09:50



谢谢,你可能只是为我节省了几个小时的挫折感 - pepsi


答案:


你的问题是 ByteBuffer.array() 返回对用作ByteBuffer的后备存储的数组的直接引用,而不是后备数组的有效范围的副本。你必须服从 bbuf.limit() (正如Peter在他的回答中所做的那样)并且只使用索引中的数组内容 0 至 bbuf.limit()-1

后备数组中额外0值的原因是CharsetEncoder创建生成的ByteBuffer的方式存在轻微缺陷。每个CharsetEncoder都有一个“每个字符的平均字节数”,对于UCS2编码器来说,它看起来简单而正确(2个字节/字符)。遵循该固定值,CharsetEncoder最初分配具有“字符串长度*每个字符的平均字节数”字节的ByteBuffer,在这种情况下,例如, 10个字符长的字符串为20个字节。然而,UCS2 CharsetEncoder以BOM(字节顺序标记)开始,它也占用2个字节,因此10个字符中只有9个符合分配的ByteBuffer。 CharsetEncoder检测溢出并分配长度为2 * n + 1的新ByteBuffer(n是ByteBuffer的原始长度),在这种情况下为2 * 20 + 1 = 41字节。由于21个新字节中只有2个需要对剩余字符进行编码,因此您可以使用该数组 bbuf.array() 将具有41个字节的长度,但是 bbuf.limit() 将指示实际仅使用前22个条目。


7
2018-05-26 09:50



谢谢,你可能只是为我节省了几个小时的挫折感 - pepsi


我不确定你是如何获得一系列的 null 字符。尝试这个

String outputEncoding = "UTF-8";
Charset charsetOutput = Charset.forName(outputEncoding);
CharsetEncoder encoder = charsetOutput.newEncoder();

// Convert the byte array from starting inputEncoding into UCS2
byte[] bufferToConvert = "Hello World! £€".getBytes();
CharBuffer cbuf = decoder.decode(ByteBuffer.wrap(bufferToConvert));

// Convert the internal UCS2 representation into outputEncoding
ByteBuffer bbuf = encoder.encode(CharBuffer.wrap(cbuf));
System.out.println(new String(bbuf.array(), 0, bbuf.limit(), charsetOutput));

版画

Hello World! £€

4
2018-05-25 16:37



但是你必须从输入编码声明一个CharsetDecoder。类似于:CharsetDecoder decoder = charsetInput.newEncoder()其中charsetInput = Charset.forName(“cp1252”)。 - robob
还有一个类似的问题: stackoverflow.com/questions/1252468/... - robob
你认为null序列可能是缺少“decoder.flush”和“encoder.flush”吗?我也看到你在代码中没有使用flush()...... - robob