前言
由于在开发中经常遇到中文乱码的问题,每次都百度解决问题就完事了,根本没有去了解其中的原理过程,总是在重复搜索浪费了很多时间。所以决定好好理解和总结一番。
本次总结主要是对编码上的一些概念的区分总结,不对具体编码的如何实现做总结,如需了解不同字符集编码实现请自行百度google搜索资料。
什么是编码?为什么要编码?
计算机只认识0和1二进制,我们人类虽然也认识二进制,但是根据01来提取信息实在是太困难了,我们有自己更方便认识的语言和文字,例如“hello world”等英文字符、“你好,中国”等中文字符等等,但是这些计算机都不认识啊,那怎么办呢?把这些字符转化成01二进制,计算机就能认识啦。
而这个转化的过程,就叫做“编码”。与之对应的逆过程,称为“解码”。
几个重要的概念
字符
“字符(Character)”是文字和符号的统称。
字库
世界上有各种语言文字和符号,古往今来,人类使用的文字和符号总会随着时代的发展而变化,我们甚至不知道世界上到底有多少文字和符号,我们不可能同时处理所有的字符,所以有必要实现规定好要使用哪些字符。这个字符的集合就叫“字库”。
字符编码
字符编码(Character Encoding) 指的是为字库中的每个字符分配一个编码值。
字符集
字符集(Character Set) 可以理解为包含字符与编码值对应的一张映射关系表。不同的字符集对同一个字符的编码值可能相同也可能不同。常见的字符集有ASCII、IOS8859、GBK、Unicode等。
字符编码方式
字符编码方式(Charater Encoding Schema),简称CES。指的是字符编码对应的内存存储方式。不同字符集的字符编码方式通常都是不同的(如ASCII,ISO-8859-1,GBK等),相同字符集的字符编码方式也可能是不同的(如UTF-8,UTF-16,UTF-32等编码方式不同,字符集都是Unicode)。
值得注意的是,我们通常所说的“××编码”通常是指字符编码方式,如:UTF-8编码。而通常在配置编码格式的配置文件中Charset、Encoding等都是指字符编码方式。
常见编码
ASCII
美国人定义的一套标准,用一个字节的低7位表示,总共容纳128个字符,包含着英文字母、数字、以及一些符号,包括换行空格等。
ISO-8859-1
ISO组织在ASCII编码的基础上进行了扩展,也是单字节编码,使用8位来表是,总共能容纳256个字符,涵盖了大部分的西欧语言字符,有ISO-8859-1(1~16)等16种不同的字符集和编码,用于西欧不同国家。
GB2312
全称《信息交换用汉字编码字符集》,使用双字节表示,共收入汉字6763个和非汉字图形字符682个。整个字符集分成94个区,每区有94个位,每个区位上只有一个字符。
GBK
全程《汉字内码扩展规范》,是GB2312的扩展,编码范围是8140~FEFE(去掉XX7F),总共有23940个码位,能表示21003个汉字,与GB2312兼容。
GB18030
全称《信息技术 中文编码字符集》,我国强制标准。由单字节、双字节或四字节编码。
基于Unicode字符集的编码方式
基于Unicode字符集的编码有UTF-8,UTF-16,UTF-16BE,UTF-16LE,UTF-32,UTF-32BE和UTF-32LE,其中UTF-16和UTF-32为复合的CES,其余为简单的CES。
何为复合CES,即UTF-16可以通过BOM头来确定究竟是UTF-16BE还是UTF-16LE。默认情况下UTF-16的BOM头为FF FE(即BE),若指定FE FF则为LE。
另外,若声明是UTF-16BE或着UTF-16LE,则不需要加BOM头以区分。
此处看不懂可先看后面……
为什么有那么多种编码?
看了这么多编码方式,肯定会想问:为什么有这么多种编码呢?
由于早起的计算机由美国人发明的,所以当时他们定义了一套自己的标准,就是ASCII编码,ASCII使用一个字节8位来表示英文字母和一些字符的,这对于美国佬来说已经足够用了。
后来,计算机发展到世界各国,而每个国家都有自己的语言,例如强大的中国,我们有自己的汉字,使用ASCII根本就不能识别汉字,所以我们就要定义自己的字符集。由于中国的汉字很多,用一个字节8位最多只能表示256个字符,根本容纳不了中国的汉字。所以就必须扩展字节数来表示,其中GB2312就是双字节编码。
Unicode的出现与发展
由于互联网的蓬勃发展,软件并不仅限于一国一语使用,很多软件都是面向国际化的,因此很容易导致单一的字符集编码无法满足多国语言情况的出现。于是便出现了ISO10646定义的的UCS2(2 byte Universal Character Set)和Unicode组织的Unicode编码,两者都是使用固定2个字节16bit来映射一个字符,实现是一样的,总共能容纳65536字符。最终决定统一Unicode标准(这也是早期java内部使用的编码,现在知道java的char类型为什么是2字节了吧)。
所以早期的UCS2和Unicode编码没有区别,使用固定的2个字节根据字节顺序的不同,又分为Unicode-16BE(大端)和Unicode-16LE(小端)两个流派,BE的方式则是从高字节到低字节的顺序,LE则是反过来。
Unicode的初衷把世界上所有的语言文字都映射到两个字节16位的空间中去,但是随着各国语言文字的收录,Unicode字符集已经收录了1,112,064个字符,已经超过了最初的65536个最大容量,于是便出现了后期的Unicode编码(也就是UTF-16),UTF-8,UTF-32等方式。
UTF-16
UCS2和早期的Unicode编码只能表示BMP平面(基础多语言平面)内的字符(也就前65536),那么超过65536的部分UTF-16使用一个辅助平面去表示,其实说白了就是超出了的部分再用2个字节表示,也就是65536之后的字符用4个字节表示。
UTF-32
UCS4、UTF-32就是使用4个字节去表示一个字符,太浪费存储空间了,一般比较少用。
UTF-8
虽然使用固定的2字节去表示字符的编码方式实现起来比较方便、容易,但在一方面来讲会浪费存储空间,另一方便则是网络上的传输问题,使用固定的2字节存在着浪费现象。有一种说法,我觉得有道理,就是说大部分的网络传输使用的是英文字符,如果这些英文字符都用2个字节表示,就很浪费,因此使用了UTF-8这种变长的编码方式会更好。
UTF-8最多可以使用6个字节去表示一个字符。UTF-8的实现规则是:
单个字节表示字符,最高位为0,因此兼容ASCII。
多字节表示字符,则多少个字符,最高字节的前多少位为1,剩余的字节前二位均为10
例如:3字节字符,则是 1110xxxx 10xxxxxx 10xxxxxx
“x”使用Unicode字符集的编码填充
Uincode编码模型
后续补充……