Java中关于字符编码的一些思考
2015-04-26 23:29
267 查看
字符编码
首先必须了解一下计算机中字符编码的概念。众所周知,计算机只认识二进制的内容,而人阅读则依赖于字符的内容,于是才有了诸如ASCII、utf-8、gbk和unicode等字符编码的产生。每一种字符编码都有一张映射表,分别记录了二进制内容 和 字符 之间的映射关系。将字符内容转为二进制内容的过程称为编码,而反之称为解码。每个字符编码都有明确支持字符的范围,比如ASCII和ISO-8859-1都不支持中文字符。
(ASCII部分映射表)
Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,并且规定所有字符固定长度为两个字节(byte)。
UTF-8的特点是对不同范围的字符使用不同长度的编码,如对字母和数字只占一个字节,而汉字占了2-4个字节的(不定长)。虽然据说UTF-8并不完全支持所有汉字,但大多数情况下它还是我们开发系统的首选。
(Unicode二进制 和 UTF-8二进制 之间的转换关系)
Java中的字符编码
由于Java是跨平台的,因此使用了Unicode字符编码。注意java中如何使用unicode其实是要深入研究的,比如所有class文件均使用了unicode编码(java文件就不一定),所有字符在jvm的内存里也是使用了unicode编码。既然Java中的字符都是以unicode编码的,那就可以解释一些常见的问题,如以下所示:
(1)char a =’中’; char在java中占两个字节,当然可以用来存储汉字(汉字在unicode中就是占两个字节)
(2)String.length() 返回的长度是字符的数量,因为String的内容保存在char[],而一个汉字也算一个字符。
Java中的String与byte[]之间的转换
在java的IO流中一般都是以byte(字节)作为传输单位的,我们从流中读到的byte[](即二进制内容)与String可通过如下关系进行转换://String -> byte[] String str = "你好!bo"; byte[] utf8_bytes = str.getBytes("UTF-8"); byte[] other_bytes = str.getBytes(); //使用JDK默认的字符集编码 //byte[] -> String String str1 = new String(utf8_bytes,"UTF-8"); String str2 = new String(other_bytes); //使用JDK默认的字符集编码注意对于JDK默认的字符集编码,若在JDK参数中没有明确指定,则window默认为GBK,linux默认为UTF-8
这里还有一个地方是值得思考的。String本质上是由char[]实现的,而char在JVM内存里表现为unicode编码。当我们在进行String与byte[]之间的转换时,其实就是Unicode编码与其他编码的之间的格式转换(具体是Unicode编码的二进制内容与其他编码的二进制内容之间的转换),我想JVM肯定是有对应的转换关系的,毕竟计算机只能认得二进制的内容。
过程如下(不一定正确):
当我们使用String.getBytes(String charsetName)方法时,其实是将String字符串表示的二进制内容(Unicode编码)转化为charsetName编码对应的二进制内容。
当我们使用new String(byte[] bytes, String charsetName) 方法时,也应该是将bytes里的二进制内容按charsetName编码与Unicode编码的映射关系转换为Unicode编码的二进制内容,这样JVM便自动将转换好的二进制内容(Unicode编码)组装为char[]进而构造成String
乱码问题产生的原因
编码和解码使用的字符编码集不一致
String str = "你好!bo"; byte[] utf8_bytes = str.getBytes("UTF-8"); String test = new String(utf8_bytes,"iso-8859-1"); //编码解码不一致导致乱码 test = new String(test.getBytes("iso-8859-1"),"UTF-8"); //矫正回正确字符
以上例子就是通讯的两端经常会出现的问题,有时候没法改去改别人的代码,那只好自己将乱码矫正回来。
解决这类问题的思路是明确String转为byte[]所用的编码,才能通过new String(byte[] bytes, String charsetName)得到正确的字符。因此在大多数系统中最好使用统一的编码如UTF-8,千万不要使用getBytes()或String(byte[] bytes)此类和平台相关的API(可能会导致在Window上没问题部署到Linux出现乱码)。
使用了错误的字符编码集进行编码
比如ISO-8859-1不支持中文,如果强行进行以下操作:byte[] bytes = "你好".getBytes("iso-8859-1");这时得到的bytes是没有办法还原回正确的字符的,因为在ios-8859-1映射表里根本找不到该字符对应的二进制内容。可以进行如下测试,得到的结果为 ??
String str = new String(bytes,"iso-8859-1")注意这种错误是不可恢复的,因为得到的bytes没有保存完整的内容,可以说这个过程是有损且不可逆的。
参考:
Java字符编码根本原理 http://lavasoft.blog.51cto.com/62575/273608/
字符集和字符编码 http://www.cnblogs.com/skynet/archive/2011/05/03/2035105.html#_4.Accept-Charset/Accept-Encoding/Ac
java中文乱码解决之道 http://easygeek.com.cn/article/AzQjMv.html
相关文章推荐
- 关于Java泛型的一些思考
- [Java]关于堆和栈的一些思考
- java学习笔记--关于interface和abstract的一些思考
- 关于JAVA中一些简单加密算法的思考
- 黑马程序员_关于JAVA中栈和堆,以及由此引发的一些思考
- 关于Java访问权限的一些思考与总结
- 关于字符编码的一些思考
- java中关于字符编码解码的思考
- Java - 关于扩展线程安全类的一些思考
- 关于Java设计之初的一些思考
- 关于“JAVA中为什么没有了多继承并出现了接口”这一问题引发的一些思考
- 关于JAVA学习的一些思考 ——记于2016.4.24午四时
- 关于java中Static的一些思考
- 关于 "java中常量定义在interface中好还是定义在class中好" 的一些思考
- 关于java与C的一些思考
- 关于java传值还是传引用的一些思考
- 关于近期学习java se篇的小结及一些学习路线的思考
- 关于Java内存溢出的一些思考
- 关于微信小程序的一些思考
- 关于产品的一些思考——知乎