大家都知道,计算机中的数据都是以二进制的形式保存的,所以如果需要在计算机中存储或者在网络上传输一些字符的话,就需要用某种规则将这些字符编排成一串串的二进制数据,就像011010101011110101101这样,这个编排的过程就叫做“字符编码”。
Unicode编码
Unicode出现之前,各个国家或地区都有自己的编码方式,非常混乱,这极大地阻碍了网络信息在全世界范围内的传播与交流。所以有有一个叫ISO的牛逼组织做了一个伟大的创想:统一天下编码方式,让信息传播归一化。这个创想的结果就是制定了Unicode编码规则。
Unicode 是为了解决传统的字符编码方案的局限而产生的,例如ISO 8859所定义的字符虽然在不同的国家中广泛地使用,可是在不同国家间却经常出现不兼容的情况。很多传统的编码方式都有一个共同的问题,即容许电脑处理双语环境(通常使用拉丁字母以及其本地语言),但却无法同时支持多语言环境(指可同时处理多种语言混合的情况)。
Unicode可以容纳世界上所有的文字和字符。Unicode目有两套编码方法,UCS-2(Unicode-16)用2个字节表示一个字符,UCS-4(Unicode-32)用4个字节表示一个字符。UCS-4是由USC-2扩展来的,增加了2字节的高位。即使是老UCS-2,它也可以表示2^16=65535个字符,基本上可以容纳所有常用各国字符,所以目前基本都使用UCS-2。
UTF8编码
Unicode使用2个字节表示一个字符,ASCII码使用1个字节,所以在很多方面产生了冲突,以前处理ASCII的方法都必须重写。而且C语言用\0作为字符串结束标志,但Unicode中很多字符都含\0,C语言的字符串函数也无法正常处理Unicode。为了把unicode投入实用,出现了UTF,最常见的是UTF-8和UTF-16。
其中UTF-16和Unicode本身的编码是一致的,都是使用两个字节编码字符,但是因为ASCII本身只需要一个字节,这就会造成空间的浪费。UTF-32和UCS-4也是相同的。
主角该出场了:UTF8是一种变长的编码,它的字节数是不固定的,使用第一个字节确定字节数。第一个字节首为0即一个字节,110即2字节,1110即3字节,字符后续字节都用10开始,这样不会混淆且单字节英文字符可仍用ASCII编码。理论上UTF-8最大可以用6字节表示一个字符,但Unicode目前没有用大于0xffff的字符,实际UTF-8最多使用了3个字节。Unicode到UTF8的转换规则如下表:
举个栗子:
‘强’字的unicode编码为:5F3A
,我们对照上表将其转换成UTF8编码:
- 5F3A在区间[0800,07FF],所以转换成UTF8之后应该是3个字节。
- 5F3A写成二进制形式是:0101 1111 0011 1010。
- 按照UTF8的模板依次从高位取4/6/6位二进制数填入模板,得到11100101 10111100 10111010。
- 转成16进制之后得到: E5 BC BA,这就是’强’字的UTF8编码。
- Java程序验证一把,程序如下:
package com.winwill.test;
/**
* @author qifuguang
* @date 15/9/6 23:37
*/
public class TestEncode {
public static void main(String[] args) throws Exception {
byte[] utf8s = "强".getBytes("utf-8");
System.out.println("编码字节数:" + utf8s.length);
System.out.print("编码结果:");
for (int i = 0; i < utf8s.length; i++) {
System.out.print(String.format("%x", utf8s[i]));
}
System.out.println();
}
}
程序输出结果为:
编码字节数:3
编码结果:e5bcba
可见,整个编码过程就是这样的,没错!