9

考虑以下代码:

byte aBytes[] = { (byte)0xff,0x01,0,0,
                  (byte)0xd9,(byte)0x65,
                  (byte)0x03,(byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07,
                  (byte)0x17,(byte)0x33, (byte)0x74, (byte)0x6f,
                   0, 1, 2, 3, 4, 5,
                   0 };
String sCompressedBytes = new String(aBytes, "UTF-16");
for (int i=0; i<sCompressedBytes.length; i++) {
    System.out.println(Integer.toHexString(sCompressedBytes.codePointAt(i)));
}

得到以下不正确的输出:

ff01, 0, fffd, 506, 717, 3374, 6f00, 102, 304, 500.

但是,如果将0xd9输入数据中的 更改为0x9d,则得到以下正确输出:

ff01, 0, 9d65, 304, 506, 717, 3374, 6f00, 102, 304, 500.

我意识到这个功能是因为字节0xd9是一个高代理 Unicode 标记。

问题:有没有办法在 Java Unicode 字符串中提供、识别和提取代理字节( 0xd800to )? 谢谢0xdfff

4

2 回答 2

11

编辑:这解决了评论中的问题

如果要对字符串中的任意二进制数据进行编码,则不应使用普通的文本编码。您没有该编码中的有效文本 - 您只有任意二进制数据。

Base64是这里的方法。Java 中没有直接的 base64 支持(无论如何是在公共类中),但是您可以使用各种 3rd 方库,例如Apache Commons Codec library 中的那个

是的,base64 会增加数据的大小 - 但它允许您稍后对其进行解码而不会丢失信息。

编辑:这解决了原始问题

我认为问题在于您没有指定适当的代理。您应该指定表示低代理项然后是高代理项的字节。之后,您应该能够添加适当的代码点。在你的情况下,你自己给了一个低代理。

这是演示这一点的代码:

public class Test
{
    public static void main(String[] args)
        throws Exception // Just for simplicity
    {
        byte[] data = 
        {
            0, 0x41, // A
            (byte) 0xD8, 1, // High surrogate
            (byte) 0xDC, 2, // Low surrogate
            0, 0x42, // B
        };

        String text = new String(data, "UTF-16");

        System.out.printf("%x\r\n", text.codePointAt(0));
        System.out.printf("%x\r\n", text.codePointAt(1));
        // Code point at 2 is part of the surrogate pair
        System.out.printf("%x\r\n", text.codePointAt(3));       
    }
}

输出:

41
10402
42
于 2009-06-08T16:50:57.340 回答
5

有没有办法在 Java Unicode 字符串中提供、识别和提取代理字节(0xd800 到 0xdfff)?

只是因为没有人提到它,我会指出Character类包括处理代理对的方法。例如isHighSurrogate(char)codePointAt(CharSequence, int)toChars(int)。我意识到这不是所述问题的重点。

new String(aBytes, "UTF-16");

这是一个将转换输入数据的解码操作。我很确定这是不合法的,因为选择的解码操作要求输入以 0xfe 0xff 或 0xff 0xfe (字节顺序标记)开头。此外,并非每个可能的字节值都可以正确解码,因为 UTF-16 是可变宽度编码

如果您想要将任意字节对称转换为 String 并返回,最好使用 8 位单字节编码,因为每个字节值都是有效字符:

Charset iso8859_15 = Charset.forName("ISO-8859-15");
byte[] data = new byte[256];
for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
  data[i - Byte.MIN_VALUE] = (byte) i;
}
String asString = new String(data, iso8859_15);
byte[] encoded = asString.getBytes(iso8859_15);
System.out.println(Arrays.equals(data, encoded));

注意:字符数将等于字节数(数据大小加倍);结果字符串不一定是可打印的(可能包含一堆控制字符)。

不过,我和 Jon 在一起——将任意字节序列放入 Java 字符串几乎总是一个坏主意。

于 2009-06-09T10:08:09.467 回答