Java字符串字符编码 – 适用于法语 – 荷兰语语言环境

我有以下代码

public static void main(String[] args) throws UnsupportedEncodingException { System.out.println(Charset.defaultCharset().toString()); String accentedE = "é"; String utf8 = new String(accentedE.getBytes("utf-8"), Charset.forName("UTF-8")); System.out.println(utf8); utf8 = new String(accentedE.getBytes(), Charset.forName("UTF-8")); System.out.println(utf8); utf8 = new String(accentedE.getBytes("utf-8")); System.out.println(utf8); utf8 = new String(accentedE.getBytes()); System.out.println(utf8); } 

以上的输出如下

 windows-1252 é ? é é 

有人可以帮我理解这是做什么的吗? 为何输​​出?

如果你已经有了一个String ,就不需要对它进行编码和解码,这个字符串已经是有人解码了原始字节的结果。

在字符串文字的情况下,某人是编译器将源读取为原始字节并使用您指定的编码对其进行解码。 如果您已经使用Windows-1252编码以物理方式保存了源文件,并且编译器将其解码为Windows-1252,那么一切都很好。 如果没有,您需要通过声明编译器在编译源时使用的正确编码来解决此问题…

这条线

 String utf8 = new String(accentedE.getBytes("utf-8"), Charset.forName("UTF-8")); 

绝对没有。 (编码为UTF-8,解码为UTF-8 == no-op)

这条线

 utf8 = new String(accentedE.getBytes(), Charset.forName("UTF-8")); 

将字符串编码为Windows-1252,然后将其解码为UTF-8。 结果必须只在Windows-1252中解码(因为在Windows-1252中编码,duh),否则你会得到奇怪的结果。

这条线

 utf8 = new String(accentedE.getBytes("utf-8")); 

将字符串编码为UTF-8,然后将其解码为Windows-1252。 同样的原则适用于以前的情况。

这条线

 utf8 = new String(accentedE.getBytes()); 

绝对没有。 (编码为Windows-1252,解码为Windows-1252 == no-op)

与可能更容易理解的整数类比:

 int a = 555; //The case of encoding as X and decoding right back as X a = Integer.parseInt(String.valueOf(a), 10); //a is still 555 int b = 555; //The case of encoding as X and decoding right back as Y b = Integer.parseInt(String.valueOf(b), 15); //b is now 1205 IE strange result 

这两个都没用,因为在做任何代码之前我们已经拥有了我们需要的东西,整数555

当字符串离开系统时需要将字符串编码为原始字节,并且当它们进入系统时需要将原始字节解码为字符串。 无需在系统内右后卫进行编码和解码。

第1行 – 系统上的默认字符集是windows-1252。

第2行 – 您通过将字符串文字编码为UTF-8字节创建了一个字符串,然后使用UTF-8方案对其进行解码。 结果是正确形成的String,可以使用windows-1252编码正确输出。

第3行 – 您通过将字符串文字编码为windows-1252创建了一个String,然后使用UTF-8对其进行解码。 UTF-8解码器检测到一个不可能是UTF-8的序列,并用问号“?”替换了有问题的字符。 (UTF-8格式表示任何顶部位设置为1的字节都是多字节字符的一个字节。但是windows-1252编码只有一个字节长……因为这是坏的UTF- 8)

第4行 – 您通过UTF-8编码创建了一个String,然后在windows-1252中解码。 在这种情况下,解码没有“失败”,但它产生了垃圾(aka mojibake)。 输出2个字符的原因是“é”的UTF-8编码是2字节序列。

第5行 – 您通过编码为windows-1252创建了一个String,并解码为windows-1252。 这产生了正确的输出。


总的教训是,如果使用一个字符编码将字符编码为字节,然后使用不同的字符编码进行解码,则可能会出现一种或另一种forms的错误。

当你调用String getBytes方法时:

使用平台的默认字符集将此String编码为字节序列,并将结果存储到新的字节数组中。

所以每当你做的时候:

 accentedE.getBytes() 

它将accentedE String的内容作为在默认OS代码页中编码的字节,在您的情况下为cp-1252 。

这一行:

 new String(accentedE.getBytes(), Charset.forName("UTF-8")) 

获取重音的E字节(在cp1252中编码)并尝试以UTF-8解码它们,因此出错。 另一方的情况相同:

 new String(accentedE.getBytes("utf-8")) 

getBytes方法接受cp-1252中编码的重音字节,用UTF-8重新编码,然后String 构造函数使用默认的OS代码页cp-1252对它们进行编码。

通过使用平台的默认字符集解码指定的字节数组构造一个新的String。 新String的长度是字符集的函数,因此可能不等于字节数组的长度。

我强烈建议您阅读这篇优秀的文章:

绝对最低每个软件开发人员绝对必须知道Unicode和字符集(没有借口!)

更新:

简而言之,每个字符都存储为数字。 为了知道哪个字符是OS使用代码页的号码。 请考虑以下代码段:

 String accentedE = "é"; System.out.println(String.format("%02X ", accentedE.getBytes("UTF-8")[0])); System.out.println(String.format("%02X ", accentedE.getBytes("UTF-8")[1])); System.out.println(String.format("%02X ", accentedE.getBytes("windows-1252")[0])); 

哪个输出:

 C3 A9 E9 

这是因为UTF-8中的小重音e存储为值C3A9两个字节,而在cp-1252中存储为值E9的单个字节。 有关详细说明,请阅读链接文章。