Java字符集

一、JVM中单个字符占用的字节长度跟编码方式有关,而默认编码方式又跟平台是一一对应的或说平台决定了默认字符编码方式;java

二、对于单个字符:ISO-8859-1单字节编码,GBK双字节编码,UTF-8三字节编码所以中文平台(中文平台默认字符集编码GBK)下一个中文字符占2个字节,而英文平台(英文平台默认字符集编码Cp1252(相似于ISO-8859-1))。程序员

三、getBytes()、getBytes(encoding)函数的做用是使用系统默认或者指定的字符集编码方式,将字符串编码成字节数组。数组

编码方式决定字节长度;在中文平台下,默认的字符集编码是GBK,此时若是使用getBytes()或getBytes("GBK"),则按照GBK的编码规则将每一个中文字符用2个byte表示因此咱们看到"中文"最终GBK编码结果就是: -42 -48 -50 -60 。-42和-48表明了"中"字,而"-50"和"-60"则表明了"文"字。浏览器

在中文平台下,若是指定的字符集编码是UTF-8,那么按照UTF-8对中文的编码规则:每一个中文用3个字节表示,那么"中文"这两个字符最终被编码成:-28 -72 -8三、-26 -106 -121两组。每3个字节表明一个中文字符。服务器

在中 文平台下,若是指定的字符集编码是ISO-8859-1,因为此字符集是单字节编码,因此使用getBytes("ISO-8859-1")时,每一个字符 只取一个字节,每一个汉字只取到了一半的字符。另一半的字节丢失了。因为这一半的字符在字符集中找不到对应的字符,因此默认使用编码63代替,也就是?。编辑器

在英文平台下,默认的字符集编码是Cp1252(相似于ISO-8859-1),若是使用GBK、UTF-8进行编码,获得的字节数组依然是正确的(GBK4个字节,UTF-8是6个字节)。由于在JVM内部是以Unicode存储字符串的,使用getBytes(encoding)会让JVM进行一次Unicode到指定编码之间的转换。对于GBK,JVM依然会转换成4个字节,对于UTF-8,JVM依然会转换成6个字节。可是对于ISO-8859-1,则因为没法转换(2个字节--->1个字节,截取了一半的字节),因此转换后的结果是错误的。函数

 

在中文平台下,默认的字符集编码是GBK,因而content.getBytes()获得的是什么呢?就是下面这4个字节:编码

byte[0] = -42 hex string = ffffffd6spa

byte[1] = -48 hex string = ffffffd0.net

byte[2] = -50 hex string = ffffffce

byte[3] = -60 hex string = ffffffc4

若是新的encoding是GBK,那么通过解码后,因为一个字符用2个字节表示。因而最终的结果就是:

char[0]='中' --- byte[0] + byte[1]

char[1]='文' --- byte[2] + byte[3]

若是新的encoding是ISO-8859-1,那么通过解码后,因为一个字符用1个字节 表示,因而原来本应该2个字节一块儿解析的变成单个字节解析,每一个字节都表明了一个汉字字符的一半。这一半的字节在ISO-8859-1中找不到对应的字 符,就变成了"?"了,最终的结果:

char[0]='?' ---- byte[0]

char[1]='?' ---- byte[1]

char[2]='?' ---- byte[2]

char[3]='?' ---- byte[3]

若是新的encoding是UTF-8,那么通过解码后,因为一个字符用3个字节表示,因而原来4个字节的数据没法正常的解析成UTF-8的数据,最终的结果也是每个都变成"?"。

char[0]='?' ---- byte[0]

char[1]='?' ---- byte[1]

char[2]='?' ---- byte[2]

char[3]='?' ---- byte[3]

若是是在英文平台下,因为默认的编码方式是Cp1252,因而content.getBytes()获得的字节都是被截去一半的残留字符,因此咱们看到在英文平台下,不论指定的encoding是GBK、UTF-8,其结果和ISO-8859-1都是同样的。

记 住:这个方法再次证实了String的getBytes()方法的危险性,若是咱们使用new String(str.getBytes(), encoding)对字符串进行从新编码解码时,咱们必定要清楚str.getBytes()方法返回的字节数组的长度、内容究竟是什么,由于在接下来使 用新的encoding进行编码解码时,Java并不会自动地对字节数组进行扩展以适应新的encoding。而是按照新的编码方法直接对该字节数组进行解析。因而结果就像上面的例子同样,一样是4个原始字节,有些每2个一组进行解析,有些每一个一组进行解析,有些每3个一组进行解析。其结果就只能看那种编码方式合适了。

 

结论:相同的平台下,同一个中文字符,在不一样的编码方式下,获得的是彻底不一样的字节数组。这些字节数组有多是正确的(只要该字符集支持中文),也多是彻底错误的(该字符集不支持中文)。

记住:不要轻易地使用或滥用String类的getBytes(encoding)方法,更要尽可能避免使用getBytes()方法。由于这个方法是平台依赖的,在平台不可预知的状况下彻底可能获得不一样的结果若是必定要进行字节编码,则用户要确保encoding的方法就是当初字符串输入时的encoding。

———————————————————————————————————————————————————

 

和getBytes(encoding)不一样,toCharArray()返回的是"天然字符"。可是这个"天然字符"的数目和内容倒是由原始的编码方式决定的

 

FileWriter是字符流输出流,而OutputStreamWriter是字节流输出流在中文平台下,如 果使用FileWriter,不论你如何设置字符集都不会起做用。由于它采用的是默认的系统字符集。即使你设置了 System.setProperty("file.encoding", "ISO-8859-1"),或者在运行时给予参数-Dfile.encoding=UTF-8都不会起做用。你会发现它最终仍是都已"GB2312"或 者"GBK"的方式保存。

在中文平台下,若是使用OutputStreamWriter,则在后台写入时会把字符流转 换成字节流,此时指定的编码字符集就起做用了。能够看到在指定GBK、UTF-8的状况下中文能够正常的保存和读取,同时文件按照咱们给定的方式保存了。 而对于ISO-8859-1则变成了?,这再次证实了采用ISO-8859-1是不能保存中文的,并且会由于中文编码在ISO-8859-1的编码中找不到对应的字符而默认转换成?。

在英文平台下,若是使用FileWriter,不论你如何设置字符集一样都不会起做用。全部 的文件都将按照ISO-8859-1的编码方式保存,毫无疑问地变成了?。在英文平台下,若是使用OutputStreamWriter,则只有当咱们把 字符和文件的编码方式正确设置为GBK、UTF-8的状况下,中文才能正确的保存并显示。

经过上述的实验证实,为了确保在不一样的平台下,客户端输入的中文能够被正确地解析、保存、读取。最好的办法就是使用OutputStreamWriter配合UTF-8编码。若是不想使用UTF-8编码,那么能够考虑使用GB2312,不建议使用GBK、GB18030。由于对于某些老式的文本编辑器,甚至不支持GBK、GB18030的编码,可是对于GB2312则是必定支持的。由于前二者都不是国标但后者是。

关于String的getBytes(),getBytes(encoding)和new String(bytes, encoding)这三个方法,很是值得注意:A.getBytes():使用平台默认的编码方式(经过file.encoding属性获取)方式来将字 符串转换成byte[]。获得的是字符串最原始的字节编码值。

B.getBytes(NAME_OF_CHARSET):使用指定的编码方式将字符串转换成byte[],若是想要获得正确的字节数组,程序员必须给出正确的NAME_OF_CHARSET。不然获得的就不会获得正确的结果。

C.new String(bytes, encoding):如 果咱们在客户端使用UTF-8编码的JSP页面发出请求,浏览器编码后的UTF-8字节会以ISO-8859-1的形式传递到服务器端。因此要获得经 HTTP协议传输的原始字节,咱们须要先调用getBytes("ISO-8859-1")获得原始的字节,但因为咱们客户端的原始编码是UTF-8,如 果继续按照ISO-8859-1解码,那么获得的将不是一个中文字符,而是3个乱码的字符。因此咱们须要再次调用new String(bytes,"UTF-8"),将字节数组按照UTF-8的格式,每3个一组进行解码,才能还原为客户端的原始字符。

D.String的getBytes()、 getBytes(NAME_OF_CHARSET)方法都是比较微妙的方法,原则上:传输时采用的是什么编码,咱们就须要按照这种编码获得字节。new String(bytes, NAME_OF_CHARSET)则更加须要当心,原则上:客户端采用的是什么编码,那么这里的NAME_OF_CHARSET就必须和客户端保持一致。例如JSP页面是GBK,那么咱们接收页面传递而来的参数时就必须使用new String(parameter.getBytes("ISO-8859-1"), "GBK");若是使用了错误 的解码方式,如使用了UTF-8,那么获得的颇有可能就是乱码了。也就是说:GBK--->ISO-8859-1--->GBK、UTF- 8--->ISO-8859-1--->UTF-8的转换过程是没有问题的。可是GBK--->ISO- 8859-1--->UTF-八、UTF-8--->ISO-8859-1--->GBK的字节直接转码则可能致使乱码,须要另外的转 换过程。

记住:谨 慎地使用getBytes(NAME_OF_CHARSET)和new String(bytes, NAME_OF_CHARSET),除非你很清楚的知道原始的字符编码和传输协议使用的编码。推荐使用基于服务器的配置、过滤器设置 request/response的characterEncoding、content type属性。还有就是JSP页面的pageEncoding属性、HTML meta元素的content type属性。尽可能避免频繁的在代码中进行字符串转码,即下降了效率又增长了风险。

相关文章
相关标签/搜索