java 奇数个字符的字符串中的 utf-8 编码字节更改

java utf-8 encoding bytes changes in string for odd number of characters

我有以下代码:

public static void main(String args[]) throws UnsupportedEncodingException {
    System.setProperty("file.encoding", "gbk");

    String name = "こんにちわ";
    String copy = new String(name.getBytes("utf-8"));

    byte[] b1 = name.getBytes("utf-8");
    byte[] b2 = copy.getBytes();

    System.out.println("b1: " + Arrays.toString(b1));
    System.out.println("b2: " + Arrays.toString(b2));
}

控制台输出为:

b1: [-29, -127, -109, -29, -126, -109, -29, -127, -85, -29, -127, -95, -29, -126, -113]
b2: [-29, -127, -109, -29, -126, -109, -29, -127, -85, -29, -127, -95, -29, -126, 63]

请注意新字符串中的最后一个字节不同。


现在,如果我改用输入 String name = "こんにち";(仅 4 个日语字符),它会变为:

b1: [-29, -127, -109, -29, -126, -109, -29, -127, -85, -29, -127, -95]
b2: [-29, -127, -109, -29, -126, -109, -29, -127, -85, -29, -127, -95]

这次字节完全一样


我在windows上使用javajdk1.6.0_45。默认字符集为 gbk。 我是否遇到了一些编码限制?

基本上,您程序的前四行相当于:

    String name = "こんにちわ";
    byte[] b1 = name.getBytes("utf-8");

    String a = new String( name.getBytes("utf-8"), "gbk" );
    byte[] b2 = a.getBytes("gbk");

也就是说,您正在获取一个字节数组 (b1),它是日语字符串的 UTF-8 表示,并告诉 Java "this byte array is in GBK encoding, convert it into a text".

这行不通,如果您打印 a 字符串,您会看到它不会打印日语文本,而是打印一些中文乱码 - 加上替换字符 ("�")结束。

在内部,Java 字符串以 UTF-16 编码。但是当你与字节数组相互转换时,你必须指定编码。编码各不相同,可能使用相同的字节值或字节值序列来表示完全不同的字符。

并且在这种情况下,UTF-8中的字节序列在GBK中是不合法的,因此,Java正在为您替换它们。

如果你想从 b1 创建一个新字符串并且它仍然是 こんにちわ,你需要创建 a 告诉 Java 这些字节是在 UTF-8 中。

    String a = new String( name.getBytes("utf-8"), "utf-8" );

那么,你的a将等于name

然后,如果您只执行 a.getBytes(),您将在 GBK 中获得表示该字符串的字节。它将 b1 不同,因为它采用不同的编码。要得到相同的数组,需要使用相同的编码(a.getBytes("utf-8")).

  • 尽量不要依赖 Java 的默认字符集。从字符串中获取字节以及将字节转换为字符串时,始终指定确切的字符集。
  • 不同的字符集为相同的字符串生成不同的字节数组。
  • 不带字符集参数的
  • getBytes()String(byte[]) 不会为您提供构成 String 基础的真实字节序列。他们使用 JVM 的默认字符集 - 在您的情况下为 GBK。

您遇到了一个常见问题,您使用的是默认平台编码,其中字节序列编码不同。 这一行

byte[] b1 = name.getBytes("utf-8");

使用 utf-8 编码将字符串转换为 byte[]。 这一行:

String a = new String( name.getBytes("utf-8"));

从字节数组创建一个字符串,但不指定字符集。这可能是个问题,因为 jvm "picks up it own value";请注意,字符串 class 还具有以下构造函数:

String(byte[] bytes, Charset charset)

它允许您指定如何使用作为第二个参数传递的编码从字节序列创建字符串。

String a = new String( name.getBytes("utf-8")); 行使用的是默认平台,从您的评论中可以看出,gbk。 所以你真正在做的是:

String a = new String( name.getBytes("utf-8"),"gbk");

而不是

String a = new String( name.getBytes("utf-8"),"UTF-8");

"tricky"部分是一些编码重叠它们转换一些(但不是全部)具有相同字节序列的符号;所以它们以某种方式代表一些字符串,但以不同的方式代表其他一些字符串。例如,ISO8859-1 以与 ISO8859-15 相同的方式表示字符,除了 € 和其他一些字符(ISO8859-15 被引入为具有单字节 enconding with € 符号,您可以看到差异 here), 因此如果字符串中不包含€符号,那么表示字符串的字节序列在ISO8859-1和ISO8859-15中是相同的。

如果你想阅读与 java 编码相关的内容以及 xml 你可以看看

不,您没有满足编码限制,但您的代码使用变量 ab2

的默认字符集

试试这个:

    String a = new String(name.getBytes("UTF-8"),"UTF-8");
    byte[] b2 = a.getBytes("UTF-8");