unicode 字符集 与 utf-8 编码
字符集
Unicode 是一个很大的集合,可以容纳100多万个符号。每个符号的编号都不一样,比如,U+0639
表示阿拉伯字母Ain
,U+0041
表示英语的大写字母A
,U+4E25
表示汉字严
。具体的符号编号对应表,可以查询unicode.org,或者专门的汉字对应表。
字符编码
有了字符集,人们就可以基于其上制定字符与二进制表示(实际上就是数字)的编码规则了。这些规则,就是字符编码。
因为字符集本身就带有一组编号(码位)。因此,字符集本身也能直接用于字符编码。例如,ASCII 既是字符集,也是字符编码;GBK 也是同样。对于这种情况,字符集和字符编码合二为一,没有任何区别。因此,当人们讲「GBK 编码」的时候,替换成「GBK 字符集」基本上不影响表意。
那么 unicode 呢?unicode 也有编号。
UCS-2(定长,采用2字节表示)
是一种直接采用 unicode 编号的编码,但是它只有16位,所以只在编号范围 u0000 到 uFFFF 范围内的编码与 Unicode 的码位完全一致。
UTF-16与UCS-2的关系UTF-16可看成是UCS-2的父集。在没有辅助平面字符(Surrogate Code Points)前,UTF-16与UCS-2所指的是同一的意思。但当引入辅助平面字符后,就称为UTF-16了。现在若有软件声称自己支持UCS-2编码,那其实是暗指它不能支持在UTF-16中超过2字节的字集。对于小于0x10000的UCS码,UTF-16编码就等于UCS码。
UTF-8(变长,采用1~3字节表示)
UCS-2 的问题:
- 由于用2个字节保存 ASCII (只需一个字节)太浪费了
- UCS-2 编码有大小端问题。
大小端序:
以汉字
严
为例,Unicode 码是4E25
,需要用两个字节存储,一个字节是4E
,另一个字节是25
。存储的时候,4E
在前,25
在后,这就是 Big endian 方式;25
在前,4E
在后,这是 Little endian 方式。Unicode 编码中表示字节排列顺序的那个文件头,叫做BOM(byte-order mark),FFFE和FEFF就是不同的BOM。UTF-8文件的BOM是“EF BB BF”,但是UTF-8的字节顺序是不变的,因此这个文件头实际上不起作用。
UTF-8 编码规则:
Unicode符号范围 | UTF-8编码方式 (十六进制) | (二进制) ----------------------+--------------------------------------------- 0000 0000-0000 007F | 0xxxxxxx 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
UTF-8 编码是变长的。除了每个字符编码的「第一个字节」之外,其余所有字节,二进制表示都以 10
开始。这样,每个字符的第一个字节就变得特殊起来。
- 一方面,首字节不以
10
开始,表达了「字符开始」这一信息; - 另一方面,除了 ASCII 范围内的单字节编码,其余多字节编码时,首字节的开始有多少位
1
,就记录了这个字符占了多少个字节
因此:
① UTF-8 编码兼容ASCII 编码。
② UTF-8 的编码规则保证了字节顺序的确定性,因此没有大端序和小端序的差异,也就不需要 BOM。
参考文章
https://liam.page/2017/08/27/mojibake-in-Windows-Notepad-due-to-wrong-encoding-detect/
http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html