单独使用字符缓冲区或逐字符转换为字节数组的编码行为差异



我正在开发一个Java应用程序,我从外部c++ dll中获得类型char[]的值。在某些情况下,期望输入非ascii值。在这种情况下,当我只通过传递从输入值的十六进制字符串解释转换成的byte[]来构造String时,它正常工作。另一方面,当我通过传递由for循环组成的字符数组来构建String时,我遇到了问题,其中每个byte都被逐一转换为char

在下面的示例中,从上述dll中获得一个char[]变量,其中输入是一个值为"但带有十六进制字符串值C3A76170

// the StringUtil.toByteArray function converts hex-string to a byte array
byte[] byteArray = StringUtil.toByteArray("C3A76170");

下面的例子给出了预期的结果:

String s1 = new String(byteArray);
// print
System.out.println(s1)
çap

下面的例子没有产生预期的结果:

char[] chars = new char[byteArray.length];
for (int i = 0; i < chars.length; i++) {
chars[i] = (char) byteArray[i];
}
String s2 = new String(chars);
// print
System.out.println(s2);
ᅢᄃap

在第二个示例中,输出是"ᅢᄃap"(其中字符"显然被误解为不同的字符)。

是什么导致了输出之间的差异?这种行为背后的原因是什么?

C和c++使用char类型来表示单个字节。然而,bytechar在Java中并不是一回事。Unicode有超过100,000个码点,因此显然单个字节无法表示所有字符。除了使用多个字节来表示某些字符之外,没有其他选择。

使用多个字节来表示单个字符的确切方法称为Charset,也称为字符编码(有时简称为"encoding")。

最流行的字符集是UTF-8,因为它是拉丁语言的紧凑表示,并且与ASCII兼容。你的c++库返回"作为UTF-8字节序列。

当您的代码执行new String(byteArray)时,它使用Charset将字节转换为字符。在Java的现代版本中,该字符集始终是UTF-8。(旧版本的Java将使用系统的默认字符集,在除Windows以外的所有系统上恰好是UTF-8。)

当您的代码执行(char) byteArray[i]时,它强制每个字节充当自己的字符,忽略了多字节序列的可能性。ç在UTF-8中表示为两个字节0xc3 0xa7。这两个字节不是单独的字符;它们合起来代表一个字符。

假设一个字节等于一个字符几乎永远是不正确的。

(另外,请随意阅读Joel关于这个主题的必读博客。)

最新更新