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

时间:2009-05-18 19:55:09

标签: java unicode utf-16 byte-order-mark

我需要在java.lang.String之间编码/解码UTF-16字节数组。字节数组以Byte Order Marker (BOM)的形式提供给我,我需要使用BOM编码字节数组。

另外,因为我正在处理Microsoft客户端/服务器,所以我想以小端(以及LE BOM)发出编码,以避免任何误解。我确实意识到使用BOM它应该可以使用big endian,但我不想在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; } 的方法:

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

在Java中执行此操作的最佳方法是什么?理想情况下,我希望避免将整个字节数组复制到一个新的字节数组中,该数组在开头分配了两个额外的字节。

解码这样的字符串同样如此,但使用java.lang.String constructor更加直截了当:

{{1}}

5 个答案:

答案 0 :(得分:27)

“UTF-16”字符集名称将始终使用BOM进行编码,并使用大/小字节顺序解码数据,但“UnicodeBig”和“UnicodeLittle”对于按特定字节顺序进行编码非常有用。使用UTF-16LE或UTF-16BE无BOM - see this post,了解如何使用“\ uFEFF”手动处理BOM。有关charset字符串名称或(最好)here类的规范命名,请参阅Charset。另请注意,绝对只需要支持limited subset of encodings

答案 1 :(得分:7)

这是你在nio中的表现方式:

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

当然应该更快,但我不知道它有多少阵列,但我对API的要点的理解是它应该最小化。

答案 2 :(得分:6)

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

对于带有BOM的小端编码,我不认为你当前的代码太糟糕了,即使是双重分配(除非你的字符串真的是怪异的)。如果它们不是处理字节数组而是处理java.nio ByteBuffer,则可能需要执行的操作,并使用java.nio.charset.CharsetEncoder类。 (你可以从Charset.forName(“UTF-16LE”)。newEncoder())获得。

答案 3 :(得分:2)

    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的了解并不熟悉如何完成这项工作。

答案 4 :(得分:0)

这是一个老问题,但我仍然无法找到可接受的答案。基本上,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();
}