UTF-8和unicode的关系
结论:
Unicode是一种规范,UTF-8是具体的实现方式。
你可以这么认为,unicode就好比Java中的接口,它只规定一些规则和内容及表示方法,UTF-8是具体的实现,考虑了很多实际的东西,比如如何存储、传输、解析、编码。UTF-16、UTF-32也是具体的实现方式,每种实现都有一些有缺点的,各自适用的场景不同,一般我们最常用的就是UTF-8:
Unicode的多种实现:UTF-8、UTF-16、UTF-32;
UTF-8是我们最常接触的,也是网络上文件最流行的,因为:采用UTF-8编码后文本体积最小;
UTF-8 采用变长方式编码文本;
UTF-8 存在:Big Endian和Little Endian问题,传说主流是Little Endian
UTF-8 存在:带BOM和不带BOM的方式,一般推荐不带BOM的文件;
Unicode编码规范
Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式公布。
它是一种标准,规定了每个字符对应的二进制数,或者称它是一个非常大的映射表。
但是unicode并没有规定字符对应的二进制数怎么存储,比如字母可能对应的数字很小,那么转换为二进制数需要8位(一个字节)就够了。但是汉字就需要14/15/16位才能表示下也就是最少需要两个字节,甚至有些字符需要三个字节才能表示。UTF-8、UTF-16、UTF-32的编码单位不同,比如对“汉字”进行编码和存储:
- UTF-8以8位为一组计数,存储“汉字”需要6个字节;
- UTF-16以16位为一组计数,存储“汉字”需要4个字节;
- UTF-32以32位为一组,存储“汉字”需要8个字节;
UTF-8的实现方式
UTF-8就是unicode的一种具体实现,它采用变长方式存储字符,比如需要一个字节就可以表示的字符那就用一个字节存储,需要二位字节表示的用两个字节存储。这样就可以尽量缩短文件大小,方便文件存储、传输。无用信息少了,文件就非常适合在网络上传输,而且能占用最少的磁盘空间。
UTF-8的编码规则:
UTF-8的编码规则很简单,只有二条:
- 对于单字节的符号(字符),字节的第一位设为0,后面7位为这个符号的unicode码;因此对于英语字母,UTF-8编码和ASCII码是相同的;
- 对于N字节的符号(N > 1),第一个字节的前N位都设为1,第N + 1位设为0,后面字节的前两位一律设为10,剩下的没有提及的二进制位,全部为这个符号的unicode码。
下表是unicode(16进制表示)编码时,对应的UTF-8编码范围。
Unicode编码(十六进制) | UTF-8编码(二进制) | UTF-8编码大小(字节) |
000000 - 00007F | 0xxxxxxx | 1 |
000080 - 0007FF | 110xxxxx 10xxxxxx | 2 |
000800 - 00FFFF | 1110xxxx 10xxxxxx 10xxxxxx | 3 |
010000 - 10FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | 4 |
跟据上表,解读UTF-8编码非常简单:
* 先读取第一个字节的第一位,如果是0,则读取一个字节,0后的7位表示为这个字符的编码,通常用16进制显示;
* 如果的第一个字节的第一位是1,则连续读取直到为0的位数,有多少个1,就表示当前字符占用多少个字节,解码的时候就读取多少个字节,然后表示为一个字符。
另外,当一个字符需要两个以上字节表示时,比如汉字“中”的unicode编码是 4E 2D,那么具体存储(传输)的时候是4E在前还是2D在前都是可以的,因此就产生了Little endian和Big endian。Little endian就是4E 2D的存储,Big endian就是2D 4E的方式。
Big Endian 和 Little Endian
名称由来
端模式(Endian)的这个词出自Jonathan Swift书写的《格列佛游记》。故事里面,小人国的人民对于吃鸡蛋是从大头敲开吃还是小头敲开吃发生了争论:主张从大头敲开吃的被称为Big Endian;主张从小头敲开吃的被称为Little Endian。两派发生了激烈的整理,并因此引发了小人国的内战。在计算机业Big Endian和Little Endian也几乎引起一场战争。
Endian表示数据在存储器中的存放顺序;
采用大端方式进行数据存放符合人类的正常思维;
而采用小端方式进行数据存放利于计算机处理。
产生原因
具体的Big Endian和Little Endian的编解码方式这里不做解释,有兴趣的可以自行google。产生原因大致如下:
- Big还是Little是由CPU引起的,涉及到两大派系:Motorola的PowerPC系列CPU和Intel的x86系列CPU;
- PowerPC系列采用Big Endian方式存储数据,而Intel的x86系列则采用Little Endian方式存储数据;
- 一般来说,大部分操作系统(如windows,FreeBSD,Linux)是Little Endian的;
- 少部分操作系统,如MAC OS是Big Endian的;
规律和建议
关于Big Endian和Little Endian到底哪个是正统哪个是旁系我们不做讨论,只需要明白并注意一下几点:
- 所有网络协议也都是采用Big Endian的方式来传输数据的。所以有时我们也会把Big endian方式称之为网络字节序;
- Java编写的程序采用Big Endian方式来存储数据;
- 没有统一建议,跟进自己的环境统一就好;
- 有说Little Endian是主流的,估计是考虑PowerPC比较少见,而且Little Endian计算更快捷吧。
带BOM和不带BOM
BOM的全称是Byte Order Mark,顾名思义就是字节序标记。BOM的设计本身是在UTF-8文件开头存储3个无意义的字节,这三个字节告诉编译器这份文件使用的是unicode编码,然后编译器可以“智能“的调用文件解码器,而不会产生乱码问题;
BOM的历史由来这里不去深究了,而且也比较复杂,BOM并不是毫无用处的,有的时候没有BOM是不行的,而且UTF-16、UTF-32也有BOM,UTF-16的BOM开头是:FF FE。
最喜欢使用BOM的是微软,它在自己的UTF-8格式的文本文件之前加上了EF BB BF三个字节,windows上面的notepad等程序就是根据这三个字节来确定一个文本文件是ASCII的还是UTF-8的,然而这个只是微软暗自作的标记,其它平台上并没有对UTF-8文本文件做个这样的标记。
关于BOM是好是坏的争论,我们这里不讨论,路遥知马力日久见人心,作为普通的RD,不求甚解就好,在实际开发中建议:
1.尽量使用不带BOM的UTF-8编码方式,尤其是网页,Unix不喜欢!;
2.微软喜欢带BOM的UTF-8,从微软迁移文件的时候要注意,尤其是shell脚本;
3.把UTF-16称为unicode也是微软特别喜欢的做法,尽量别这样,把JVM称为Java总之是不合适的;
4.BOM并不是一无是处,不要一味的否定它,有的时候必须带BOM;