Как кодировать/декодировать массивы байтов UTF-16LE с помощью спецификации?
Мне нужно кодировать/декодировать массивы байтов UTF-16 в и из java.lang.String
. Байт-массивы даны мне с Byte Order Marker (BOM), и мне нужно закодировать байтовые массивы с помощью спецификации.
Кроме того, поскольку я имею дело с клиентом/сервером Microsoft, я бы хотел испустить кодировку в маленьком endian (вместе с LE BOM), чтобы избежать каких-либо недоразумений. Я действительно понимаю, что с помощью спецификации он должен работать с большим энтузиазмом, но я не хочу плавать вверх по потоку в мире Windows.
В качестве примера, здесь приведен метод, который кодирует a 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)
Ответы
Ответ 1
Обозначение кодировки "UTF-16" всегда будет кодироваться с помощью спецификации и декодировать данные с использованием большой/малой энсианности, но "UnicodeBig" и "UnicodeLittle" полезны для кодирования в определенном порядке байтов. Используйте UTF-16LE или UTF-16BE без спецификации - см. Этот пост о том, как использовать "\uFEFF" для обработки спецификаций вручную. См. здесь для канонического именования строковых имен символов или (предпочтительно) Charset. Также обратите внимание, что требуется только поддержка ограниченного подмножества кодировок.
Ответ 2
Так вы делаете это в nio:
return Charset.forName("UTF-16LE").encode(message)
.put(0, (byte) 0xFF)
.put(1, (byte) 0xFE)
.array();
Конечно, он должен быть быстрее, но я не знаю, сколько массивов он делает под обложками, но мое понимание точки API заключается в том, что он должен минимизировать это.
Ответ 3
Во-первых, для декодирования вы можете использовать набор символов "UTF-16"; который автоматически определяет начальную спецификацию. Для кодирования UTF-16BE вы также можете использовать набор символов "UTF-16", который будет писать правильную спецификацию, а затем выводить файлы большого размера.
Для кодирования с небольшим значком с спецификацией я не думаю, что ваш текущий код слишком плохой, даже с двойным распределением (если только ваши строки не являются чудовищными). Что бы вы могли сделать, если они есть, это не дело с байтовым массивом, а скорее java.nio ByteBuffer и использование класса java.nio.charset.CharsetEncoder. (Который вы можете получить из Charset.forName( "UTF-16LE" ). NewEncoder()).
Ответ 4
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, чтобы узнать, как это сделать.
Ответ 5
Это старый вопрос, но, тем не менее, я не мог найти приемлемого ответа для моей ситуации. В принципе, 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();
}