我需要在
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。 对于编码 UTF-16BE,您还可以使用“UTF-16”字符集 - 这将编写正确的 BOM,然后输出大端字节序内容。
对于使用 BOM 编码为小端,我不认为你当前的代码太糟糕,即使使用双重分配(除非你的字符串真的很可怕)。 如果它们是,您可能想要做的不是处理字节数组,而是处理 java.nio ByteBuffer,并使用 java.nio.charset.CharsetEncoder 类。 (您可以从 Charset.forName("UTF-16LE").newEncoder() 获得。
这就是你在 nio 中的做法:
return Charset.forName("UTF-16LE").encode(message)
.put(0, (byte) 0xFF)
.put(1, (byte) 0xFE)
.array();
它当然应该更快,但我不知道它在幕后生成了多少个数组,但我对 API 要点的理解是它应该最大限度地减少数组。
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();
}
为了从 String 转换为 byte[],强制使用带有顺序标记的小端或大端,我使用 Apache 的通用语言 ArrayUtils 提出了以下 1 行解决方案:
tmp = ArrayUtils.addAll(new byte[] {(byte) 0xFF, (byte) 0xFE}, message.getBytes(UTF_16LE))
tmp = ArrayUtils.addAll(new byte[] {(byte) 0xFE, (byte) 0xFF}, message.getBytes(UTF_16BE))