如何使用BOM对UTF-16LE字节数组进行编码/解码?

我需要在java.lang.String编码/解码UTF-16字节数组。 字节数组通过字节顺序标记(BOM)提供给我,我需要使用BOM编码字节数组。

此外,因为我正在处理Microsoft客户端/服务器,我想以小端(以及LE BOM)发出编码以避免任何误解。 我确实意识到使用BOM它应该工作大端,但我不想在Windows世界游泳上游。

作为一个例子,这里有一个方法,它使用BOM将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”字符集名称将始终使用BOM进行编码,并使用大/小字节顺序解码数据,但“UnicodeBig”和“UnicodeLittle”对于按特定字节顺序进行编码非常有用。 使用UTF-16LE或UTF-16BE无需BOM – 请参阅此文章 ,了解如何使用“\ uFEFF”手动处理BOM。 请参阅此处了解charset字符串名称或(最好) Charset类的规范命名。 另请注意,绝对需要支持有限的编码子集 。

这就是你在nio中的表现:

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

它当然应该更快,但我不知道它在封面下制作了多少个数组,但我对API的要点的理解是它应该最小化它。

首先,对于解码,您可以使用字符集“UTF-16”; 自动检测初始BOM。 对于UTF-16BE编码,你也可以使用“UTF-16”字符集 – 它会写出一个合适的BOM,然后输出大端的东西。

对于带有BOM的小端编码,我不认为你当前的代码太糟糕了,即使是双重分配(除非你的字符串真的是怪异的)。 如果它们不是处理字节数组而是处理java.nio ByteBuffer,则可能需要执行的操作,并使用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没有内置的带有BOM的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(); }