java utf-8编码字节在字符串中为奇数个字符而改变

时间:2015-07-06 11:23:35

标签: java encoding utf-8

我有以下代码:

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中的最后一个字节是不同的。

现在,如果我使用输入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上使用java jdk1.6.0_45 。默认字符集为gbk。 我遇到了一些编码限制吗?

3 个答案:

答案 0 :(得分:2)

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

    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“此字节数组采用GBK编码,将其转换为文本”。

这不起作用,如果你打印a字符串,你会发现它不会打印日文文本,而是打印一些中文乱码 - 加上最后的替换字符(“ ”)。

在内部,Java字符串以UTF-16编码。但是当您转换为字节数组和从字节数组转换时,您必须指定编码。编码彼此不同,并且可以使用相同的字节值或字节值序列来表示完全不同的字符。

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

如果要从b1创建一个新字符串并使其仍为こんにちわ,则需要创建a告诉Java该字节是UTF-8。< / p>

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

然后,您的a将等于name

然后,如果你只是a.getBytes(),你将获得代表 GBK 中该字符串的字节。它将与<{em}不同,因为它采用不同的编码方式。要获得相同的数组,您需要使用相同的编码(b1)。

  • 尽量不要依赖Java的默认字符集。当从字符串中获取字节时,以及将字节转换为字符串时,始终指定确切的字符集。
  • 不同的字符集为同一个字符串生成不同的字节数组。
  • 没有charset参数的
  • a.getBytes("utf-8")getBytes()不会为您提供String(byte[])下的真实字节序列。它们使用JVM的默认字符集 - 在您的情况下为GBK。

答案 1 :(得分:1)

您正面临一个常见问题,即使用默认平台编码,并使用不同编码的字节序列。 这一行

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

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

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

从字节数组创建一个字符串,但不指定字符集。这可能是一个问题,因为jvm&#34;拿起它自己的价值&#34 ;;请注意,String类还有以下构造函数:

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");

&#34;棘手&#34;部分是一些编码重叠,即他们用一些相同的字节序列转换一些(但不是全部)符号;所以它们以某种方式表示一些字符串,但是其他一些字符串以不同的方式表示。例如,ISO8859-1以与ISO8859-15相同的方式表示字符,除了€和其他一些字符(引入ISO8859-15以使用单个字节包含€符号,您可以看到差异{{3如果字符串不包含€符号,则字节序列表示ISO8859-1和ISO8859-15中相同的字符串。

如果您想通过xml阅读与java编码相关的内容,可以查看here

答案 2 :(得分:1)

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

的默认字符集

试试这个:

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