Как кодировать / декодировать байтовые массивы UTF-16LE с помощью спецификации?

мне нужно кодировать / декодировать массивы UTF-16 байтов в и из java.lang.String. Массивы байтов даются мне с маркер порядка байтов (BOM), и мне нужно закодировать массивы байтов со спецификацией.

кроме того, поскольку я имею дело с клиентом/сервером Microsoft, я хотел бы испустить кодировку в little endian (вместе с LE BOM), чтобы избежать каких-либо недоразумений. Я понимаю, что с BOM он должен работать big endian, но я не хочу плавать вверх по течению в окнах мир.

в качестве примера, вот метод, который кодирует java.lang.String as UTF-16 в маленьком endian с BOM:

public static byte[] encodeString(String message) {

    byte[] tmp = null;
    try {
        tmp = message.getBytes("UTF-16LE");
    } catch(UnsupportedEncodingException e) {
        // should not possible
        AssertionError ae =
        new AssertionError("Could not encode UTF-16LE");
        ae.initCause(e);
        throw ae;
    }

    // use brute force method to add BOM
    byte[] utf16lemessage = new byte[2 + tmp.length];
    utf16lemessage[0] = (byte)0xFF;
    utf16lemessage[1] = (byte)0xFE;
    System.arraycopy(tmp, 0,
                     utf16lemessage, 2,
                     tmp.length);
    return utf16lemessage;
}

каков наилучший способ сделать это на Java? В идеале я хотел бы избежать копирования всего массива байтов в новый массив байтов, который имеет два дополнительных байта, выделенных в начале.

то же самое касается декодирования такой строки, но это гораздо проще, используя java.lang.String конструктор:

public String(byte[] bytes,
              int offset,
              int length,
              String charsetName)

5 ответов


имя кодировки " UTF-16 "всегда будет кодироваться с помощью BOM и будет декодировать данные, используя либо big/little endianness, но" UnicodeBig "и" UnicodeLittle " полезны для кодирования в определенном порядке байтов. Используйте UTF-16LE или UTF-16BE для No BOM -этот пост Как использовать "\uFEFF " для обработки BOMs вручную. См.здесь для канонического именования имен строк кодировки или (предпочтительно)Charset класса. Также обратите внимание, что только ограниченное подмножество кодировки совершенно необходимы, что поддержаны.


вот как вы это делаете в nio:

    return Charset.forName("UTF-16LE").encode(message)
            .put(0, (byte) 0xFF)
            .put(1, (byte) 0xFE)
            .array();

это, конечно, должно быть быстрее, но я не знаю, сколько массивов он делает под обложками, но мое понимание точки API заключается в том, что он должен минимизировать это.


во-первых, для декодирования вы можете использовать набор символов "UTF-16"; который автоматически обнаруживает начальную спецификацию. Для кодирования UTF-16BE вы также можете использовать набор символов" UTF-16 " - это напишет правильную спецификацию, а затем выведет большой материал endian.

для кодирования в little endian с BOM, я не думаю, что ваш текущий код слишком плох, даже с двойным распределением (если ваши строки действительно чудовищны). То, что вы можете сделать, если они не имеют дело с массивом байтов, но а на Java.nio ByteBuffer и используйте java.НИО.кодировка.Класс CharsetEncoder. (Который вы можете получить от кодировки.forName ("UTF-16LE").newEncoder()).


    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(string.length() * 2 + 2);
    byteArrayOutputStream.write(new byte[]{(byte)0xFF,(byte)0xFE});
    byteArrayOutputStream.write(string.getBytes("UTF-16LE"));
    return byteArrayOutputStream.toByteArray();

EDIT: перечитывая ваш вопрос, я вижу, что вы предпочли бы избежать двойного распределения массива вообще. К сожалению, API не дает вам этого, насколько я знаю. (Был метод, но он устарел, и вы не можете указать кодировку с ним).

Я написал выше, прежде чем я увидел ваш комментарий, Я думаю, что ответ на использование классов nio находится на правильном пути. Я смотрел на это, но я недостаточно знаком с API, чтобы знать, как вы это получаете сделанный.


Это старый вопрос, но все же, я не мог найти приемлемого ответа для моей ситуации. В принципе, Java не имеет встроенного кодера для UTF-16LE со спецификацией. Итак, вы должны развернуть свою собственную реализацию.

вот что я закончил:

private byte[] encodeUTF16LEWithBOM(final String s) {
    ByteBuffer content = Charset.forName("UTF-16LE").encode(s);
    byte[] bom = { (byte) 0xff, (byte) 0xfe };
    return ByteBuffer.allocate(content.capacity() + bom.length).put(bom).put(content).array();
}