代码之家  ›  专栏  ›  技术社区  ›  Jared Oberhaus

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

  •  21
  • Jared Oberhaus  · 技术社区  · 15 年前

    我需要对UTF-16字节数组进行编码/解码 java.lang.String . 字节数组以 Byte Order Marker (BOM) ,我需要用BOM表对字节数组进行编码。

    另外,因为我正在处理一个Microsoft客户机/服务器,所以我希望使用little endian(以及LE BOM)进行编码,以避免任何误解。我确实意识到,有了BOM,它应该可以大端运行,但我不想在Windows世界中逆流而上。

    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 constructor :

    public String(byte[] bytes,
                  int offset,
                  int length,
                  String charsetName)
    
    5 回复  |  直到 15 年前
        1
  •  31
  •   McDowell    15 年前

    “UTF-16”字符集名称将始终使用BOM进行编码,并将使用大/小尾端对数据进行解码,但“UnicodeBig”和“UnicodeLittle”对于以特定字节顺序进行编码非常有用。使用UTF-16LE或UTF-16BE表示无BOM- see this post 了解如何使用“\uFEFF”手动处理BOM。看见 here Charset 班还要注意的是,只有 limited subset of encodings

        2
  •  7
  •   Yishai    15 年前

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

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

        3
  •  6
  •   Daniel Martin    15 年前

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

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

        4
  •  3
  •   Yishai    15 年前
        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();
    

    在看到您的评论之前,我写了上述内容,我认为使用nio类的答案是正确的。我已经看过了,但是我对API还不太熟悉,不知道您是如何做到这一点的。

        5
  •  0
  •   hopia    7 年前

    以下是我最终得到的结果:

    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();
    }