24

我需要将 UTF-16 字节数组编码/解码到java.lang.String. 字节数组是通过Byte Order Marker (BOM)提供给我的,我需要使用 BOM 对字节数组进行编码。

此外,因为我正在处理 Microsoft 客户端/服务器,所以我想以小端序(连同 LE BOM)发出编码以避免任何误解。我确实意识到使用 BOM 应该可以使用大端,但我不想在 Windows 世界中逆流而上。

例如,这里有一个用 BOM 将 a 编码java.lang.StringUTF-16little endian 的方法:

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)
4

5 回答 5

31

“UTF-16”字符集名称将始终使用 BOM 进行编码,并使用大/小字节序对数据进行解码,但“UnicodeBig”和“UnicodeLittle”对于以特定字节顺序进行编码很有用。对无 BOM 使用 UTF-16LE 或 UTF-16BE -请参阅这篇文章了解如何使用“\uFEFF”手动处理 BOM。有关 charset 字符串名称或(最好) Charset类的规范命名,请参见此处。另请注意,仅绝对需要支持有限的编码子集。

于 2009-05-18T20:08:45.157 回答
7

这就是你在 nio 中的做法:

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

它当然应该更快,但我不知道它在幕后制作了多少个数组,但我对 API 的理解是它应该将其最小化。

于 2009-05-18T23:09:56.650 回答
6

首先,对于解码,您可以使用字符集“UTF-16”;自动检测初始 BOM。对于 UTF-16BE 编码,您还可以使用“UTF-16”字符集 - 这将编写一个正确的 BOM,然后输出大端的东西。

对于使用 BOM 编码为小端序,我认为您当前的代码不会太糟糕,即使使用双重分配(除非您的字符串真的很可怕)。如果它们不是处理字节数组而是处理 java.nio ByteBuffer,您可能想要做的事情,并使用 java.nio.charset.CharsetEncoder 类。(您可以从 Charset.forName("UTF-16LE").newEncoder() 获得)。

于 2009-05-18T20:15:47.453 回答
3
    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 不够熟悉,无法立即知道你是如何完成的。

于 2009-05-18T20:09:49.897 回答
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();
}
于 2017-08-24T22:17:10.220 回答