字符编码详解
一、一些概念
在开始将字符编码之前,必须了解几个概念。字符集(Character set))和编码(Encoding)就是理解字符编码的最重要的两个概念。字符集,顾名思义就是字符的集合。而字符编码就是将字符映射成一个数字,实质是用数字代表字符,从而让计算机能够理解。如下我们可以自己进行对字符集编码。另外的重要概念还有乱码,乱码是指使用错误的编码规则解析实际的机器代码生成一些特殊字符的过程。
字符集: {a,b,c,d,e} 编码: 97 --> a 98 --> b 98 --> c 99 --> d 100 --> e
二、字符编码的诞生与ASCII
谈到字符编码的诞生,那就必须说一下计算机的历史。计算机最早是在美国诞生,刚开始诞生的时候,只有位的概念,晶体管的高位与低位分别用1和0表示。但随着计算机的发展,需要表示字符,为了表示更多的字符,开始产生编码。最初的编码是4位的BCD编码,6位的BCD编码(BCDIC),接着产生了至今仍在广泛使用的7位ASCII。ASCII(American Standard Code for Information Interchange,美国标准信息交换代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。ASCII由7位表示数字、字母、特使的控制字符,但是还有其他字符需要加入,于是再加1位,总共8位表示字符。从此,确定了,使用8位是一个字符存储单位,于是诞生了1字节=8位。字节从原始的意思,就是指一个完整的字符。
最开始的ASCII,称为标准ASCII,使用一个字节中除去最高位(最高位为0)以外的7 位来表示所有的大写和小写字母,数字0到9、标点符号,以及在美式英语中使用的特殊控制字符。在计算机初期,世界上所有的计算机都用同样的ASCII方案来保存英文字符,没有什么中文字符、日文字符。 标准ASCII字符编码如下图所示
然而标准ASCII仅仅够美国使用,其他欧洲语言无法加入,于是开始使用最高位进行扩展,扩展后的ASCII,后128位(128~255)称为扩展字符,前128位(0~127)不变。然而如何扩展字符,产生了不同意见。于是诞生了ISO8859标准,ISO8859制定了多个字符集与多个编码,如下所示
ISO 8859-1 (Latin-1) - 西欧语言
ISO 8859-2 (Latin-2) - 中欧语言
ISO 8859-3 (Latin-3) - 南欧语言。世界语也可用此字符集显示。
ISO 8859-4 (Latin-4) - 北欧语言
ISO 8859-5 (Cyrillic) - 斯拉夫语言
ISO 8859-6 (Arabic) - 阿拉伯语
ISO 8859-7 (Greek) - 希腊语
ISO 8859-8 (Hebrew) - 希伯来语(视觉顺序)
ISO 8859-8-I - 希伯来语(逻辑顺序)
ISO 8859-9 (Latin-5 或 Turkish) - 它把Latin-1的冰岛语字母换走,加入土耳其语字母。
ISO 8859-10 (Latin-6 或 Nordic) - 北日耳曼语支,用来代替Latin-4。
ISO 8859-11 (Thai) - 泰语,从泰国的 TIS620 标准字集演化而来。
ISO 8859-13 (Latin-7 或 Baltic Rim) - 波罗的语族
ISO 8859-14 (Latin-8 或 Celtic) - 凯尔特语族
ISO 8859-15 (Latin-9) - 西欧语言,加入Latin-1欠缺的法语及芬兰语重音字母,以及欧元符号。
ISO 8859-16 (Latin-10) - 东南欧语言。主要供罗马尼亚语使用,并加入欧元符号。
扩展后的ASCII最为流行的是ISO 8859-1,同样可以称为Latin-1。
三、中国自己的字符编码的诞生
但随着计算机的普及与中国的发展,中国也开始有了自己的计算机,自然而然地想使用自己的文字,于是创建自己国家的字符集,并进行编码,然而1个字节已经无法再使用,于是使用两个字节表示中文字符,创建了GB2312编码。GB2312在兼容了标准ASCII的基础上,GB2312收录了简化汉字、符号、字母、日文假名等共计7445个字符,其中汉字占6763个。GB2312将代码表分区94个区(0xA1-0xFE),对应第一个字节,每个区94个位(0xA1-0xFE),对应了第二字节,两个字节的值分别为区号的值和位号的值加32(0x20),因此也被称为区位码。GB2312的编码范7围是0x2121-0x777E,与ASCII有重叠,通常方法是将GB码的两个字节的最高位置1区别。
然而GB2312的出现,基本满足了汉字的计算机处理需要,但对于人名、古汉语等方面出现的罕用字,GB2312不能处理,于是产生了GBK编码,GBK即汉字内码扩展规范,K为汉语拼音 Kuo Zhan(扩展)中“扩”字的声母。英文全称Chinese Internal Code Specification。GBK共收入21886个汉字和图形符号,同样以双字节表示。
虽然GBK以及满足大部分要求,但是开始要加入少数民族的字符,2个字节不够用,于是产生新的国家标准,产生了GB18030。GB18030编码采用多自己编码,是现在推荐使用的编码,汉字收录范围包含繁体汉字以及日韩汉字。它是一二四字节变长编码,其中包括:
- 单字节,其值从0到0x7F,与 ASCII 编码兼容。
- 双字节,第一个字节的值从0x81到0xFE,第二个字节的值从0x40到0xFE(不包括0x7F),与 GBK标准基本兼容。
- 四字节,第一个字节的值从0x81到0xFE,第二个字节的值从0x30到0x39,第三个字节从0x81到0xFE,第四个字节从0x30到0x39。
四、国际统一字符编码的诞生
- Unicode的诞生
随着计算机传入各国,每个国家开始有自己的不同字符编码,比如台湾编码为BIG5。不同国家之间于是存在着重叠,无法支持多语言办公,不方便进行各国进行交流。于是出现了Unicode,Unicode是计算机行业统一的编码标准。既然Unicode是编码标准,那肯定需要字符集,它是在统一字符集UCS(Universal Character Set)进行制定的标准,UCS包含全世界不同国家的字符,并还在不断收录。至目前为止的第六版,Unicode 就已经包含了超过十万个字符。
因为Unicode是在全球的各国的通用字符制定的标准,所以又称为统一码、万国码等。它的目标是将既有的字符编码方案以Unicode 编码方案来加以取代,特别是既有的方案在多语环境下,皆仅有有限的空间以及不兼容的问题。
2. UCS-2与UTF-16
Unicode可以通过不同的字符编码进行实现,最常使用的字符编码是UTF-8、UTF-16与现在已经过时了的UCS-2。因为最开始的时候,字符还没那么多,实际上大多数人不会用到超过前65535个以外的字符。因此使用2个字节进行编码,称为UCS-2。但随着Unicode收录的字符不断增多,实际上,2个字节以及不够用了,于是产生了UTF-16,UTF是指Unicode Transformation Format的缩写,意为Unicode转换格式。UTF-16采用变长编码,1个或2个16位编码表示一个Unicode字符。UTF-16是在继承了UCS-2的基础上,以16位无符号整数为单位,可能有2个16位,但绝大多数情况与UCS-2相同,只使用2个字节。
但问题又来了,在不同体系的计算机系统中,编码的Unicode字符在内存中存储的顺序是不同的。使用Inter生产的CPU的计算机,内存中数据存储通常是低字节在前,高字节在后,这种存储方式被称为Little-Endian。在对于一些计算机,内存中数据存储通常是高字节在前,低字节在后,这种存储方式被称为Big-Endian。
UTF-16编码的文件通常在文件开头用字符标志出使用的存储方式:若文件开头是“0xFF 0xFF”,表示文件其余部分是Little-Endian的 UTF-16编码;若文件开头是“0xFE 0xFF”,表示文件其余部分是Big-Endian的UTF-16编码。
3. UTF-8
虽然说以及有了UCS-2与UTF-16,但是传输效率不够高,于是产生了UTF-8,当然还有其他种种原因,促进了UTF-8的诞生。UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码(定长码),也是一种前缀码。它可以用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容,这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。
UTF-8用1到6个字节编码UNICODE字符,UTF-8是ASCII的一个超集。因为一个纯ASCII字符串也是一个合法的UTF-8字符串,所以现存的ASCII文本不需要转换。为传统的扩展ASCII字符集设计的软件通常可以不经修改或很少修改就能与UTF-8一起使用。而多文种平面(BMP)中的字符(这包含了大部分常用字)使用三个字节编码,其中中文就使用3个字节进行编码。
五、Java与字符编码
Java的实际的字符编码的转换过程如上图所示。