我需要在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}}
答案 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();
}