Преобразование из Windows 1252 в UTF8 в Java: пустые символы с 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?

Ответ 1

Ваша проблема заключается в том, что ByteBuffer.array() возвращает прямую ссылку на массив, используемый в качестве резервного хранилища для ByteBuffer, а не на копию допустимого диапазона массива поддержки. Вы должны подчиняться bbuf.limit() (как это сделал Питер в своем ответе) и просто использовать содержимое массива от индекса 0 до bbuf.limit()-1.

Причина дополнительных значений 0 в массиве поддержки - это небольшой недостаток в том, как созданный ByteBuffer создается CharsetEncoder. Каждый CharsetEncoder имеет "средний байт на символ", который для кодера UCS2 кажется простым и правильным (2 байта / char). Следуя этому фиксированному значению, CharsetEncoder изначально выделяет ByteBuffer с байтами "длина строки * средний байт на символ", в данном случае, например, 20 байтов для строки длиной 10 символов. UCS2 CharsetEncoder запускает, однако, спецификацию (байтовый порядок байтов), который также занимает 2 байта, так что только 9 из 10 символов вписываются в выделенный ByteBuffer. CharsetEncoder обнаруживает переполнение и выделяет новый ByteBuffer длиной 2 * n + 1 (n - исходная длина ByteBuffer), в этом случае 2 * 20 + 1 = 41 байт. Поскольку для кодирования оставшегося символа требуется только 2 из 21 нового байта, массив, который вы получаете из bbuf.array(), будет иметь длину 41 байт, но bbuf.limit() будет указывать, что фактически используются только первые 22 записи.

Ответ 2

Я не уверен, как вы получите последовательность символов 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! £€