标签:
前言
字符编码,这本不属于IO的内容,但字节流之后写的应该是字符流,既然是字符流,那就涉及一个"字符编码的"问题,考虑到字符编码不仅仅是在IO这块,Java中很多场景都涉及到这个概念,因此这边文章就专门详细写一下字符编码,具体的网上有很多,但本文目的是尽量讲清楚各种编码方式的作用,个人认为,不求、也没有必要对字符编码理解地多么深入。
字符集和字符编码
第一个概念就是字符集和字符编码之间的区别:
1、字符集(charset)
字符集指的是一个系统支持的所有抽象字符的集合。字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等,常见的字符集有ASCII字符集、GB2312字符集、BIG5字符集、GB18030字符集、Unicode字符集等。
2、字符编码(encoding)
计算机要准确处理各种字符集文字,就要进行字符编码,以便计算机能够识别和存储各种文字。因此字符编码就是讲符号转换为计算机可以接受的数字系统的数,称为数字代码。
ASCII码
计算机里面只有数字0和1(严格说连0和1都没有,只有开和关,无非是用0和1表示开关的状态罢了),在计算机软件里的一切都是用数字标识的额,屏幕上显示的一个一个字符也是数字。最初使用的计算机在美国,用到的字符很少,因此每一个字符都用一个数字表示,一个字节所能表示的数字反内卫足以容纳所有这些字符。实际上表示这些字符的数字的字节最高位都是0,也就是说这些数字都在0~127之间,如字符a对应97,字符b对应数字98,这种字符与数字的对应编码固定下来之后,这套编码规则被称为ASCII码(美国标准信息交换码)。一张简单的ASCII码表如图:
GB2312和GBK
随着计算机在其它国家的普及,许多国家把本地字符集引入了计算机,大大扩展了计算机中字符的范围。一个字节所能表示的范围不足以容纳中文字符(看看上面的ASCII码表就知道了),中国大陆将每一个中文字符都用两个字节表示,原有的ASCII码字符的编码保持不变。
为了将一个中文字符与两个ASCII码字符相区别,中文字符的每个字节最高位为1,中国大陆为每一个中文字符都指定了一个对应的数字,并于1980年制定了一套汉字内码规范,这套规范就是GB2312,GB2312包含了ASCII码所包含的英文字符,同时加入了6763个简体汉字以及其他一些ASCII码之外的符号。
GBK是在1995年制定的后续标准,它在GB2312的基础上对更多的中文字符(包括繁体)、日文的假名进行了编码。GBK还是现如今中文Windows操作系统的系统默认编码。
Unicode
在一个国家的本地化系统中出现的一个字符,通过电子邮件传送到另外一个国家的本地化系统中,看到的就不是那个原始字符了,而是另外那个国家的一个字符或乱码,因为计算机里面并没有真正的字符,字符都是以数字的形式存在的,通过邮件传送一个字符,实际上传送的是这个字符对应的字符编码,同一个数字在不同的国家和地区代表的很可能是不同的符号。
为了解决各个国家和地区之间各自使用不同的本地化字符编码带来的不便,人们将全世界所有的符号进行了统一编码,称之为Unicode(统一码、万国码)。所有字符不再区分国家和地区,都是人类共有的符号,如"中"字在Unicode中不再是GBK中的D6D0,而是在任何地方都是4e2d,如果所有的计算机系统都使用这种编码方式,那么4e2d这个字在任何地方都代表汉字中的"中"。Unicode编码的字符都占用两个字节的大小,也就是说全世界所有字符个数不会超过65536个。
当然Unicode只包含65536个字符就想包含全世界所有的字符是远远不够的,所以Unicode提供了字符平面映射,链接地址上就是Wiki百科对于字符平面映射的解读。
UTF-8和UTF-16
Unicode是一种字符集标准,而具体该标准应该如何应用到计算机中,则是另一个话题了,常用的Unicode编码方式有两种:
1、UTF-16。两个字节表示Unicode转换格式,这是定长的表示方法。也就是说不管什么字符都可以使用两个字节表示,两个字节是16Bit,所以叫做UTF-16。UTF-16编码非常方便,每两个字节表示一个字符,这个在字符串操作时大大简化了操作。
2、UTF-8。UTF-16统一采用了两个字节表示一个字符,虽然在表示上非常简单,但是很大一部分字符用一个字节表示就够了,现在需要两个字节,存储空间放大了一倍。UTF-8就采取了一种变长技术,每个编码区域有不同的字码长度,不同了类型的字符可以是由1~6个字节组成。
Java与字符编码
Java中的字符使用的都是Unicode编码,Java技术在通过Unicode保证跨平台特性的前提下也支持了全扩展的本地平台字符集,而显示输出和键盘输入都是采用的本地编码。因此,免不了二者的转化问题。
看一个很简单的例子:
public static void main(String[] args) throws Exception { // 这里将字符串通过getBytes()方法,编码成GB2312 byte b[] = "大家一起来学习Java语言".getBytes("GB2312"); File file = new File("D:/Files/encoding.txt"); OutputStream out = new FileOutputStream(file); out.write(b); out.close(); }
看一下文件中是什么:
正常输出,无编码问题。但是如果这样:
public static void main(String[] args) throws Exception { // 这里将字符串通过getBytes()方法,编码成GB2312 byte b[] = "大家一起来学习Java语言".getBytes("ISO8859-1"); File file = new File("D:/Files/encoding.txt"); OutputStream out = new FileOutputStream(file); out.write(b); out.close(); }
再看一下文件中是什么:
乱码问题就出现了,这主要是由于JDK设置环境变量引起的,我们用程序看一下JDK环境变量:
public static void main(String[] args) { System.getProperties().list(System.out); }
看一下输出的全部信息,有点长:
1 -- listing properties -- 2 java.runtime.name=Java(TM) SE Runtime Environment 3 sun.boot.library.path=E:\MyEclipse10\Common\binary\com.sun.... 4 java.vm.version=11.3-b02 5 java.vm.vendor=Sun Microsystems Inc. 6 java.vendor.url=http://java.sun.com/ 7 path.separator=; 8 java.vm.name=Java HotSpot(TM) 64-Bit Server VM 9 file.encoding.pkg=sun.io 10 user.country=CN 11 sun.java.launcher=SUN_STANDARD 12 sun.os.patch.level= 13 java.vm.specification.name=Java Virtual Machine Specification 14 user.dir=F:\代码\MyEclipse\TestIO 15 java.runtime.version=1.6.0_13-b03 16 java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment 17 java.endorsed.dirs=E:\MyEclipse10\Common\binary\com.sun.... 18 os.arch=amd64 19 java.io.tmpdir=C:\Users\dell1\AppData\Local\Temp20 line.separator= 21 22 java.vm.specification.vendor=Sun Microsystems Inc. 23 user.variant= 24 os.name=Windows Vista 25 sun.jnu.encoding=GBK 26 java.library.path=E:\MyEclipse10\Common\binary\com.sun.... 27 java.specification.name=Java Platform API Specification 28 java.class.version=50.0 29 sun.management.compiler=HotSpot 64-Bit Server Compiler 30 os.version=6.2 31 user.home=C:\Users\dell1 32 user.timezone= 33 java.awt.printerjob=sun.awt.windows.WPrinterJob 34 file.encoding=GBK 35 java.specification.version=1.6 36 user.name=dell1 37 java.class.path=F:\代码\MyEclipse\TestIO\bin 38 java.vm.specification.version=1.0 39 sun.arch.data.model=64 40 java.home=E:\MyEclipse10\Common\binary\com.sun.... 41 java.specification.vendor=Sun Microsystems Inc. 42 user.language=zh 43 awt.toolkit=sun.awt.windows.WToolkit 44 java.vm.info=mixed mode 45 java.version=1.6.0_13 46 java.ext.dirs=E:\MyEclipse10\Common\binary\com.sun.... 47 sun.boot.class.path=E:\MyEclipse10\Common\binary\com.sun.... 48 java.vendor=Sun Microsystems Inc. 49 file.separator=50 java.vendor.url.bug=http://java.sun.com/cgi-bin/bugreport... 51 sun.cpu.endian=little 52 sun.io.unicode.encoding=UnicodeLittle 53 sun.desktop=windows 54 sun.cpu.isalist=amd64
注意一下34行,表明了JDK使用的是GBK(GBK是GB2312上的扩展,所以用GB2312字符集当然是没有问题的),既然JDK用的GBK,那么用ISO8859-1当然是有问题的。
标签:
原文地址:http://www.cnblogs.com/xrq730/p/4889593.html