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

StackOverflow https://stackoverflow.com/questions/879482

Вопрос

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

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

В качестве примера, вот метод, который кодирует java.lang.String как UTF-16 в литтл эндиане со спецификацией:

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)
Это было полезно?

Решение

Кодировка "UTF-16" всегда будет кодироваться с помощью спецификации и будет декодировать данные, используя либо большой, либо маленький порядковый номер, но "UnicodeBig" и "UnicodeLittle" полезны для кодирования в определенном порядке байтов.Используйте UTF-16LE или UTF-16BE без спецификации - смотрите этот пост о том, как использовать "\uFEFF" для обработки спецификаций вручную.Видишь здесь для канонического именования имен строк кодировки или (предпочтительно) Кодировка класс.Также примите к сведению, что только ограниченное подмножество кодировок абсолютно необходимы для поддержки.

Другие советы

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

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

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

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

Для кодирования в little endian с помощью спецификации я не думаю, что ваш текущий код слишком плох, даже с двойным распределением (если только ваши строки действительно не чудовищны).Что вы, возможно, захотите сделать, если они есть, так это иметь дело не с массивом байтов, а с байтовым буфером java.nio и использовать java.nio.charset.Класс CharsetEncoder .(Который вы можете получить из Charset.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();

Редактировать:Перечитывая ваш вопрос, я вижу, что вы предпочли бы вообще избежать двойного выделения массива.К сожалению, 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();
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top