编码由两大类,一类是非Unicode编码,一类是Unicode编码。
常见非Unicode编码
ASCII
世界上虽然有各种各样的的字符,但是计算机发明之初没有考虑那么多,基本只考虑了美国的需求。美国大概只需要128个字符,所以就规定了128个字符的二进制表示方法,这个方法是一种标准,即ASCII编码。
128个字符用7位刚好可以表示,计算机存储的最小单位是byte,即8位,ASCII码中最高位设置为0,剩下7为表示字符。这7位可以看作0~127,ASCII码规定了从0~127的每个数据代表什么含义。
ASCII编码,可打印字符:
java代码打印如下:
public static void main(String[] args) {
for (int i = 32; i < 127; i++) {
System.out.print((char)i);
}
}
ASCII编码,控制字符(不可打印):
ASCII码对美国来说是够用了,但对其他国家来说是不够的,搜易各个国家的各种计算机厂商就发明的各种各样编码方式来表示自己国家的字符。
为了保证与ASCII码的兼容性,一般都是将最高位设置为1。也就是说,当最高位为0时,表示ASCII码,为1时,表示自己国家的字符。
在西欧国家中流行的是ISO 8859-1和Windows-1252,在中国则是GB2312、GBK、GB18030、Big5
ISO 8859-1
ISO 8859-1又称Latin-1,也是用一个字节表示一个字符,其中0~127与ASCII码一样,128~255规定了不同的含义。128~159表示一些控制字符。160~255表示一些西欧字符。如下
Windows-1252
ISO 8859-1虽然号称是标准,用于西欧国家,但是它连欧元这个符号都没有。实际上使用最为广泛的是Windows-1252编码,这个编码与ISO 8859-1基本是一样的,区别只在于128~159。Windows-1252使用其中的一些数字表示可打印字符。
这个编码中加入了欧元符号以及其他一些常用字符,基本认为ISO 8859-1被Windows-1252取代。很多程序中,尽管文件被声明为ISO 8859-1编码,解析的时候被当作Windows-1252编码来解析。
HTML5中明确规定,如果文件声明为ISO 8859-1编码,它会被看作Windows-1252编码。
GB2312
美国和西欧字符用一个字节就够了,但中文是不够的,中文的第一个标准就是GB2312。GB2312主要针对的是简体中文常见字符,包括约7000个汉字和一些罕用词和繁体字。
GB2312使用两个字节表示汉字,在两个字节中,最高位都是1,如果是0,就认为是ASCII字符
GBK
GBK兼容GB2312的基础上,向下兼容GB2312。GB2312编码的字符和二进制表示,在GBK编码里是完全一样的。GBK增加了一万四千多个汉字。
GB18030、Big5就不再介绍了。
Unicode编码
上面介绍了一些常用非Unicode编码。这些编码基本都忽略了其他国家的字符和编码,甚至忽略了同一国家的其他计算机厂商,这样造成的结果就是,出现太多编码方式,且相互不兼容。
Unicode编码应运而生。
Unicode编码做了一件事,给世界上所有的字符都分配了一个唯一的数字编号,这个编号范围从0x0000000x10FFFF,110多万个。但常用字符在0x00000xFFFF之间,即65536个数字之内。每个字符都有一个Unicode编号,这个编号一般写为16进制,在前面加U+。大部分中文编号范围为U+4E00~U+9FFF。
Unicode给所有字符分配了唯一数字编号,但它并没有规定这个编号怎么对应到二进制表示。这是与上面介绍的其他编码所不同的。
那么编号是怎么对应到二进制表示呢?有多种方案。主要有UTF-32、UTF-16、UTF-8。
UTF-32
这个最简单,就是字符编号的整数二进制形式,4个字节。
但是有一个细节,就是字节的排列顺序,如果第一个字节是整数二进制中的最高位,最后一个字节是整数二进制中的最低位,那这种字节序就叫“大端”,否则叫“小端”。对应的编码方式分别是UTF-32BE和UTF-32LE
UTF-32非常浪费空间,实际上采用的也很少。
UTF-16
- 编号在
U+0000—U+FFFF
的字符(常用字符集),直接用两个字节表示。 - 编号在
U+10000—U+10FFFF
之间的字符,需要用四个字节表示。
UTF-8
UTF-8是使用最广泛的Unicode编码方式,它是一种可变长的编码方式,可以是1—4个字节不等,它可以完全兼容ASCII码的128个字符。
注意: UTF-8 是一种编码方式,Unicode是一个字符集合。
UTF-8的编码规则:
对于单字节的符号,字节的第一位为0,后面的7位为这个字符的Unicode编码,因此对于英文字母,它的Unicode编码和ACSII编码一样。
对于n字节的符号,第一个字节的前n位都是1,第n+1位设为0,后面字节的前两位一律设为10,剩下的没有提及的二进制位,全部为这个符号的Unicode码 。
我们来看一下具体的Unicode编号范围与对应的UTF-8二进制格式 :
编码范围(编号对应的十进制数) | 二进制格式 |
---|---|
0x00—0x7F (0-127) | 0xxxxxxx |
0x80—0x7FF (128-2047) | 110xxxxx 10xxxxxx |
0x800—0xFFFF (2048-65535) | 1110xxxx 10xxxxxx 10xxxxxx |
0x10000—0x10FFFF (65536以上) | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
那我们该如何通过具体的Unicode编码,进行具体的UTF-8编码呢? |
步骤:
找到该Unicode编码的所在的编号范围,进而找到与之对应的二进制格式
将Unicode编码转换为二进制数(去掉最高位的0)
将二进制数从右往左一次填入二进制格式的X中,如果有X未填,就设为0
我们来看一个实际的例子:
“马” 字的Unicode编码是:0x9A6C
,整数编号是39532
(1)首选确定了该字符在第三个范围内,它的格式是 1110xxxx 10xxxxxx 10xxxxxx
(2)39532对应的二进制数为1001 1010 0110 1100
(3)将二进制数填入X中,结果是:11101001 10101001 10101100