Java字符串的字符编码 - 对于法国 - 荷兰的语言环境

问题描述:

我有下面这段代码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 
é 
? 
é 
é 

有人可以帮助我了解这是否做什么?为什么这个输出?

+0

为了获得预期输出,请确保为该文件设置文件编码类型'UTF8'。如果您使用的是eclipse,请右键单击文件选择属性,然后选择utf8作为文本文件编码类型。 – user964147 2013-03-19 13:21:34

如果您已有String,则无需对其进行编码和解码,该字符串已经是某人解码原始字节的结果。

在字符串文字的情况下,某人是编译器将您的源代码读取为原始字节并将其解码为您指定的编码。如果您已经使用Windows-1252编码实际保存了源文件,并且编译器将其解码为Windows-1252,则一切正常。如果没有,你需要通过声明正确的编码,编译器编译源时使用,以解决这个问题...

线

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

绝对没有。 (编码为UTF-8,解码为UTF-8 ==无操作)

线

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

串进行编码作为视窗-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 ==无操作)

比喻用整数可能更容易理解:

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 I.E. strange result 

这两者都是无用的,因为我们已经拥有了我们需要做任何的代码之前,整数555

有必要对 编码的字符串转换成原始字节,当它会使系统和有需要的原始字节解码成字符串时,他们进入你的系统。没有必要在系统内对进行编码和解码。

+0

“(编码为Windows-1252,解码为Windows-1252 == no-op)” - 不正确。它将破坏Windows 1252中不可用的所有字符,并将它们变成问号。 – 2014-09-06 21:11:31

+0

@KarolS你正在脱离上下文听起来聪明大声笑 – Esailija 2014-09-09 08:51:18

当你呼吁字符串getBytes方法是:

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

所以每当你做的事:

accentedE.getBytes() 

需要accentedE String的内容作为默认的操作系统代码页字节encoded,你的情况cp-1252

这条线:

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

取accentedE字节(CP1252编码),并试图将它们以UTF-8进行解码,因此,该错误。从另一面同样的情况为:

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

的getBytes方法发生在CP-1252编码的accentedE字节,再编码它们以UTF-8但随后的字符串constructor与默认OS代码页是CP对它们进行编码-1252。

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

我强烈建议阅读这篇精彩文章:

The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

UPDATE:

总之,每一个字符存储为一个数字。为了知道哪个字符是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。有关详细解释,请阅读链接的文章。

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

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

第3行 - 通过将字符串文字编码为windows-1252,然后使用UTF-8对其进行解码来创建字符串。 UTF-8解码器检测到一个不可能是UTF-8的序列,并用问号“?”取代了违规字符。 (UTF-8格式表示最高位设置为1的任何字节都是多字节字符的一个字节,但windows-1252编码只有一个字节长。这是不好的UTF-8)

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

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


占总体的教训是,如果你编码字符一个字符编码字节,然后用编码你是容易获得某种形式的重整不同的字符进行解码。